Error Handling¶
RequestError¶
request() does not raise on non-2xx status codes by default — it returns an ApiResponse with the status code. Inspect the status yourself:
response = await client.request("GET", "/api/dcim/devices/99/")
if response.status == 404:
print("Device not found")
elif response.status == 403:
print("Forbidden — check your token")
elif response.status >= 500:
print(f"Server error: {response.text}")
elif response.status == 200:
device = response.json()
RequestError is raised only by get_version():
from netbox_sdk import RequestError
try:
version = await client.get_version()
except RequestError as exc:
print(f"HTTP {exc.response.status}: {exc.response.text}")
The facade layer raises RequestError for failed endpoint operations such as
await nb.dcim.devices.get(1) when the response is not a handled success case.
Facade-specific exceptions¶
The higher-level facade adds a few explicit exceptions for common NetBox SDK workflows.
ParameterValidationError¶
Raised when strict filter validation is enabled and a filter name is not present in the OpenAPI schema:
from netbox_sdk import ParameterValidationError, api
nb = api("https://netbox.example.com", token="tok", strict_filters=True)
try:
nb.dcim.devices.filter(not_a_real_filter="value")
except ParameterValidationError as exc:
print(exc)
AllocationError¶
Raised for allocation-style detail endpoints when NetBox returns HTTP 409:
from netbox_sdk import AllocationError
prefix = await nb.ipam.prefixes.get(123)
try:
await prefix.available_prefixes.create({"prefix_length": 28})
except AllocationError as exc:
print(exc)
ContentError¶
Raised when the facade expects JSON but the server returns invalid content:
from netbox_sdk import ContentError
try:
plugins = await nb.plugins.installed_plugins()
except ContentError:
print("NetBox returned invalid JSON")
ConnectionProbe¶
Use probe_connection() before any API calls to validate connectivity and surface human-readable errors:
probe = await client.probe_connection()
if not probe.ok:
# Common cases:
# probe.status == 0 → network unreachable (DNS, TCP, TLS failure)
# probe.status == 404 → base_url points to wrong path
# probe.status == 401 → auth rejected (invalid token)
print(f"Cannot reach NetBox (HTTP {probe.status}): {probe.error}")
return
print(f"NetBox API version: {probe.version}")
ConnectionProbe fields:
| Field | Type | Meaning |
|---|---|---|
ok |
bool |
True if NetBox is reachable (status < 400, or 403) |
status |
int |
HTTP status code; 0 on network-level failure |
version |
str |
Value of the API-Version response header |
error |
str \| None |
Human-readable error, or None if ok is True |
Note: 403 counts as ok=True because it means the URL is valid — only the token is wrong.
Network errors¶
Network-level failures (DNS failure, TCP timeout, TLS error) are raised as exceptions from request(). The client catches them internally when a stale cache entry exists; otherwise they propagate:
import aiohttp
try:
response = await client.request("GET", "/api/dcim/devices/")
except aiohttp.ClientConnectorError as exc:
print(f"Cannot connect: {exc}")
except TimeoutError:
print("Request timed out")
If a stale cache entry exists for the failed request, request() returns the stale response with X-NBX-Cache: STALE instead of raising.
Timeout configuration¶
The default timeout is 30 seconds. Adjust per connection:
from netbox_sdk import Config
cfg = Config(
base_url="https://netbox.example.com",
token_version="v1",
token_secret="tok",
timeout=10.0, # 10 second timeout
)
Missing configuration¶
Check completeness before making calls:
from netbox_sdk.config import is_runtime_config_complete
cfg = Config(base_url="https://nb.example.com", token_version="v2", token_secret="s")
if not is_runtime_config_complete(cfg):
# For v2: missing token_key
# For v1: missing token_secret
# For both: missing base_url
raise RuntimeError("Incomplete configuration")
build_url() raises RuntimeError("NetBox base URL is not configured") if base_url is None.
Schema resolution errors¶
from netbox_sdk.services import resolve_dynamic_request
try:
req = resolve_dynamic_request(idx, "dcim", "typo", "list", ...)
except ValueError as exc:
print(exc) # "Resource not found: dcim/typo"
try:
req = resolve_dynamic_request(idx, "dcim", "devices", "get", object_id=None, ...)
except ValueError as exc:
print(exc) # "Action 'get' requires --id"
JSON parse errors¶
ApiResponse.json() raises json.JSONDecodeError if the response body is not valid JSON. Always check the status code first — error responses (4xx/5xx) are sometimes HTML:
response = await client.request("GET", "/api/dcim/devices/")
if response.status == 200:
data = response.json()
else:
print(f"Error {response.status}: {response.text[:200]}")
The facade wraps this case as ContentError when a higher-level operation
requires JSON decoding.
Typed SDK validation errors¶
The typed client adds explicit Pydantic-backed validation failures.
TypedRequestValidationError¶
Raised before HTTP when the request body does not match the versioned request model for the selected NetBox release line.
from netbox_sdk import TypedRequestValidationError, typed_api
nb = typed_api("https://netbox.example.com", token="tok", netbox_version="4.5")
try:
await nb.ipam.prefixes.available_ips.create(7, body=[{"prefix_length": "invalid"}])
except TypedRequestValidationError as exc:
print(exc)
TypedResponseValidationError¶
Raised after HTTP when NetBox returns JSON that does not match the expected typed response model.