Error Handling
Both SDKs provide custom exception hierarchies so you can catch specific error types instead of parsing HTTP status codes.
Python Error Hierarchy
All exceptions inherit from OnaError:
OnaError
├── ConfigurationError — invalid SDK config (missing endpoint, wrong scheme)
├── ServiceUnavailableError — 5xx server errors or network failures
├── ValidationError — invalid request parameters (client-side check)
├── AuthenticationError — 401/403 access denied
├── ResourceNotFoundError — 404 resource not found
├── RateLimitError — 429 too many requests
└── TimeoutError — request timed out
Usage
from ona_platform import OnaClient
from ona_platform.exceptions import (
OnaError,
ConfigurationError,
ServiceUnavailableError,
ValidationError,
AuthenticationError,
ResourceNotFoundError,
RateLimitError,
TimeoutError,
)
client = OnaClient()
try:
records = client.inverter_telemetry.get_inverter_telemetry(
asset_id='INV-001',
site_id='Sibaya',
time_range={'start': '2025-11-01T00:00:00', 'end': '2025-11-01T12:00:00'},
limit=100,
)
except ResourceNotFoundError as e:
print(f"Site not found: {e}")
except AuthenticationError as e:
print(f"Access denied: {e}")
except RateLimitError as e:
print(f"Rate limit exceeded: {e}")
except ValidationError as e:
print(f"Invalid request: {e}")
except ServiceUnavailableError as e:
print(f"Service error: {e}")
except OnaError as e:
print(f"SDK error: {e}")
JavaScript Error Hierarchy
All errors inherit from OnaSDKError:
OnaSDKError
├── APIError — API request failed (includes statusCode + response)
├── ConfigurationError — invalid config (includes missingFields)
├── ValidationError — input validation failed (includes field + value)
├── AuthenticationError — authentication failed
└── TimeoutError — request timed out (includes timeout ms)
Usage
const { OnaSDK } = require('./src/index');
const {
OnaSDKError,
APIError,
ConfigurationError,
ValidationError,
AuthenticationError,
TimeoutError,
} = require('./src/utils/errors');
const sdk = new OnaSDK({
endpoints: {
inverterTelemetry: process.env.INVERTER_TELEMETRY_ENDPOINT,
},
inverterTelemetryApiKey: process.env.INVERTER_TELEMETRY_API_KEY,
});
try {
const records = await sdk.inverterTelemetry.getInverterTelemetry({
asset_id: 'INV-001',
site_id: 'Sibaya',
time_range: { start: '2025-11-01T00:00:00', end: '2025-11-01T12:00:00' },
limit: 100,
});
} catch (error) {
if (error instanceof ValidationError) {
console.error('Validation error:', error.field, error.message);
} else if (error instanceof AuthenticationError) {
console.error('Authentication failed:', error.message);
} else if (error instanceof APIError) {
if (error.statusCode === 429) {
console.error('Rate limit exceeded');
} else if (error.statusCode >= 500) {
console.error('Service unavailable:', error.statusCode);
} else {
console.error('API error:', error.statusCode, error.message);
}
} else if (error instanceof TimeoutError) {
console.error('Request timed out after', error.timeout, 'ms');
} else {
console.error('Unknown error:', error);
}
}
Retry Logic
Python
The SDK uses exponential backoff via the retry_with_backoff decorator. Retries apply to ServiceUnavailableError and TimeoutError only — not to auth, validation, or rate-limit errors.
from ona_platform.utils.retry import retry_with_backoff
# Default: 3 retries, 2.0 backoff factor (2s, 4s, 8s)
# The decorator can be applied to any function
@retry_with_backoff(max_retries=3, backoff_factor=2.0)
def my_api_call():
...
Configure retry behavior at the client level:
client = OnaClient(max_retries=5, retry_backoff=2.5)
The telemetry and OODA clients implement their own retry loop for 5xx responses with the same exponential backoff:
# Internal retry logic (InverterTelemetryClient._get_with_retry)
for attempt in range(1, max_retries + 2):
resp = session.get(url, params=params, timeout=30)
if resp.status_code >= 500 and attempt <= max_retries:
time.sleep(2 ** (attempt - 1)) # 2s, 4s, 8s...
continue
return handle_response(resp)
JavaScript
The SDK retries failed requests with configurable delay:
const sdk = new OnaSDK({
retries: 3, // default: 3
retryDelay: 1000, // default: 1000ms
});
Cost Protection
Both SDKs enforce client-side limits to prevent accidental cost overruns. These checks run before any network call:
| Limit | Value | Exception on violation |
|---|---|---|
| Max records per query | 1000 | ValidationError |
| Max time range per query | 31 days | ValidationError |
| Min polling interval (streaming) | 5 seconds | ValidationError |
# Python — raises ValidationError before hitting the API
client.inverter_telemetry.get_inverter_telemetry(
asset_id='INV-001',
site_id='Sibaya',
time_range={'start': '2025-01-01', 'end': '2025-03-15'}, # > 31 days
limit=5000, # > 1000
)
# ValidationError: limit must not exceed 1000
Best Practices
-
Catch specific exceptions first. Always order your
exceptblocks from most specific to most general (ResourceNotFoundErrorbeforeOnaError). -
Don’t retry on 4xx. The SDK only retries 5xx and timeout errors. Auth, validation, and rate-limit errors are not retried — handle them explicitly.
-
Handle rate limits gracefully. When you catch
RateLimitError(Python) orAPIErrorwith status 429 (JavaScript), back off and retry after a delay. The limit is 60 requests per minute. -
Validate locally before uploading. Use the Python SDK’s
validate_batch()to check ODS-E compliance before calling the ingestion service — this avoids wasting API calls on invalid records. -
Use configuration errors for fail-fast setup. Wrap client initialization in a try/except for
ConfigurationErrorto give users clear setup guidance:try: client = OnaClient() except ConfigurationError as e: print(f"Setup incomplete: {e}") print("See https://docs.asoba.co/sdk/installation") sys.exit(1)
Error Reference
| HTTP Status | Python Exception | JavaScript Error | Meaning |
|---|---|---|---|
| 400 | ValidationError |
ValidationError |
Invalid parameters |
| 401 | AuthenticationError |
AuthenticationError |
Invalid or missing API key |
| 403 | AuthenticationError |
AuthenticationError |
Key not scoped to site |
| 404 | ResourceNotFoundError |
APIError (404) |
Resource not found |
| 429 | RateLimitError |
APIError (429) |
Rate limit exceeded (60 req/min) |
| 5xx | ServiceUnavailableError |
APIError (5xx) |
Server error (auto-retried) |
| — | TimeoutError |
TimeoutError |
Request timed out |
| — | ConfigurationError |
ConfigurationError |
Missing endpoint or key |
Next Steps
- Service Guides — browse individual service documentation
- Authentication — API key setup
- Installation — full configuration reference