Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions backend/docs/bailo.core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,33 @@ bailo.core package
:show-inheritance:
:exclude-members: ValuedEnum

Error Handling
--------------

All API calls raise :class:`~bailo.core.exceptions.BailoException` when the
backend returns an error response. The exception carries structured detail from
the server:

* ``status_code`` - the HTTP status code (e.g. ``400``, ``404``).
* ``message`` - the human-readable error message.
* ``context`` - a dict of additional detail. For schema validation failures this
includes a ``validationErrors`` list describing each field that failed.

.. code-block:: python

from bailo.core.exceptions import BailoException

try:
client.put_model_card(model_id=model_id, metadata=invalid_metadata)
except BailoException as e:
print(e.status_code) # 400
print(e.message) # "Model metadata could not be validated..."
for err in e.context["validationErrors"]:
print(f" {err['property']}: {err['message']}")

# Or simply print the formatted error:
print(e)

.. automodule:: bailo.core.exceptions
:members:
:undoc-members:
Expand Down
8 changes: 8 additions & 0 deletions backend/docs/bailo.helper.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
bailo.helper package
====================

.. note::

All helper methods raise :class:`~bailo.core.exceptions.BailoException` on
API errors. These exceptions include the HTTP status code, error message, and
full error context from the backend (e.g. per-field validation errors for
model card or datacard updates). See the :mod:`bailo.core.exceptions` module
documentation for details and usage examples.


.. automodule:: bailo.helper.access_request
:members:
Expand Down
40 changes: 15 additions & 25 deletions backend/docs/notebooks/datacards_demo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,7 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Install dependencies...\n",
"! pip install bailo\n",
"\n",
"# Necessary import statements\n",
"from bailo import Datacard, Client\n",
"\n",
"# Instantiating the PkiAgent(), if using.\n",
"from bailo import PkiAgent\n",
"agent = PkiAgent(cert='', key='', auth='')\n",
"# Instantiating the TokenAgent(), if using.\n",
"from bailo import TokenAgent\n",
"agent = TokenAgent(access_key='', secret_key='')\n",
"\n",
"# Instantiating the Bailo client\n",
"client = Client(\"http://127.0.0.1:8080\", agent) # <- INSERT BAILO URL (if not hosting locally)"
]
"source": "# Install dependencies...\n! pip install bailo\n\n# Necessary import statements\nfrom bailo import Datacard, Client\nfrom bailo.core.exceptions import BailoException\n\n# Instantiating the PkiAgent(), if using.\nfrom bailo import PkiAgent\nagent = PkiAgent(cert='', key='', auth='')\n# Instantiating the TokenAgent(), if using.\nfrom bailo import TokenAgent\nagent = TokenAgent(access_key='', secret_key='')\n\n# Instantiating the Bailo client\nclient = Client(\"http://127.0.0.1:8080\", agent) # <- INSERT BAILO URL (if not hosting locally)"
},
{
"attachments": {},
Expand Down Expand Up @@ -134,13 +118,7 @@
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"If successful, the above will have created a new datacard, and the `data_card_version` attribute should be set to 1.\n",
"\n",
"Next, we can populate the data using the `update_data_card()` method. This can be used any time you want to make changes, and the backend will create a new datacard version each time. We will learn how to retrieve datacards later (either the latest, or a specific release).\n",
"\n",
"NOTE: Your datacard must match the schema, otherwise an error will be thrown."
]
"source": "If successful, the above will have created a new datacard, and the `data_card_version` attribute should be set to 1.\n\nNext, we can populate the data using the `update_data_card()` method. This can be used any time you want to make changes, and the backend will create a new datacard version each time. We will learn how to retrieve datacards later (either the latest, or a specific release).\n\nNOTE: Your datacard must match the schema, otherwise a `BailoException` will be raised. The exception includes the HTTP status code, an error message, and detailed validation errors showing exactly which fields failed. See the error handling example below."
},
{
"cell_type": "code",
Expand All @@ -160,6 +138,18 @@
"print(f\"Datacard version is {datacard.data_card_version}.\")"
]
},
{
"cell_type": "markdown",
"source": "### Handling datacard validation errors\n\nIf the datacard metadata does not conform to the schema (e.g. it contains extra fields not allowed by the schema), the client raises a `BailoException` with detailed information. You can inspect the `status_code`, `message`, and `context` attributes to understand exactly what went wrong.",
"metadata": {}
},
{
"cell_type": "code",
"source": "invalid_card = {\n 'overview': {\n 'storageLocation': 'S3',\n },\n 'not_in_schema': 'This field is not allowed by the schema',\n}\n\ntry:\n datacard.update_data_card(data_card=invalid_card)\nexcept BailoException as e:\n print(f\"Status code: {e.status_code}\")\n print(f\"Message: {e.message}\")\n print(f\"Validation errors: {e.context['validationErrors']}\")\n print()\n print(\"Formatted error:\")\n print(e)",
"metadata": {},
"execution_count": null,
"outputs": []
},
{
"attachments": {},
"cell_type": "markdown",
Expand Down Expand Up @@ -220,4 +210,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
36 changes: 35 additions & 1 deletion backend/docs/notebooks/models_and_releases_demo_pytorch.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"source": [
"# Necessary import statements\n",
"from bailo import Model, Client\n",
"from bailo.core.exceptions import BailoException\n",
"import torch\n",
"from torchvision.models import resnet50, ResNet50_Weights\n",
"\n",
Expand Down Expand Up @@ -188,7 +189,7 @@
"\n",
"Next, we can populate the model card using the `update_model_card()` method. This can be used any time you want to make changes, and the backend will create a new model card version each time. We will learn how to retrieve model cards later (either the latest, or a specific release).\n",
"\n",
"NOTE: Your model card must match the schema, otherwise an error will be thrown."
"NOTE: Your model card must match the schema, otherwise a `BailoException` will be raised. The exception includes the HTTP status code, an error message, and detailed validation errors showing exactly which fields failed. See the error handling example below."
]
},
{
Expand Down Expand Up @@ -217,6 +218,39 @@
"If successful, the `model_card_version` will now be 2!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Handling model card validation errors\n",
"\n",
"If the model card metadata does not conform to the schema (e.g. it contains extra fields not allowed by the schema), the client raises a `BailoException` with detailed information. You can inspect the `status_code`, `message`, and `context` attributes to understand exactly what went wrong."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"invalid_card = {\n",
" 'overview': {\n",
" 'modelSummary': 'ResNet-50 model for image classification.',\n",
" },\n",
" 'not_in_schema': 'This field is not allowed by the schema',\n",
"}\n",
"\n",
"try:\n",
" model.update_model_card(model_card=invalid_card)\n",
"except BailoException as e:\n",
" print(f\"Status code: {e.status_code}\")\n",
" print(f\"Message: {e.message}\")\n",
" print(f\"Validation errors: {e.context['validationErrors']}\")\n",
" print()\n",
" print(\"Formatted error:\")\n",
" print(e)"
]
},
{
"attachments": {},
"cell_type": "markdown",
Expand Down
13 changes: 11 additions & 2 deletions lib/python/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@

All dates are formatted dd/mm/yyyy.

## 3.7.2 - dd/mm/2026
## 3.8.0 - dd/mm/2026

### Changes

- Improve Python error handling for non-success connected `Client` responses.
- Correct some docstrings.
- Add error handling to Jupyter Notebook docs.
- Update dependencies.

## 3.7.2 - 11/05/2026

- Fix boolean to string conversion for API requests.
- Extend unit & integration test coverage
- Extend unit & integration test coverage.

## 3.7.1 - 27/04/2026

Expand Down
2 changes: 1 addition & 1 deletion lib/python/src/bailo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import logging

# Package Version
__version__ = "3.7.2"
__version__ = "3.8.0"


from bailo.core.agent import Agent, PkiAgent, TokenAgent
Expand Down
8 changes: 4 additions & 4 deletions lib/python/src/bailo/core/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ def __request(self, method, *args, **kwargs):
return res

try:
# Give the error message issued by bailo
payload = res.json()
message = payload.get("error", {}).get("message", "Unknown API error")
raise BailoException(message)
error_body = payload.get("error", {})
message = error_body.get("message", "Unknown API error")
context = error_body.get("context")
raise BailoException(message=message, status_code=res.status_code, context=context)
except JSONDecodeError as e:
# No response given
raise ResponseException(f"{res.status_code} Cannot {method} to {res.request.url}") from e

def get(self, *args, **kwargs):
Expand Down
Loading
Loading