Skip to main content

Error handling

Exception hierarchy

OnehouseSdkError
└── ResourcesError # base for the resources/ subpackage
├── AuthError # missing/invalid credentials, HTTP 401/403
├── SqlParseError # HTTP 400 — server rejected the SQL
├── OperationFailedError # terminal status FAILED or INVALID
└── OperationTimeoutError # polling exceeded the configured timeout

Catch ResourcesError if you want a single net for everything the resources client can throw. Catch the specific subclasses when you want to handle a category differently (e.g. retry on timeout, surface SQL errors to a user).

What each error means

AuthError

The credential set is incomplete, malformed, or rejected by the server.

Sources:

  • Missing required fields when constructing the client (you'll see exactly which fields and where to set them).
  • World-readable credentials file (warning, not error — but worth fixing).
  • HTTP 401 or 403 from the server (key/secret rotated, link revoked, wrong region).

Handle by: halting and surfacing the message — it tells you which field is missing and which env var / profile to set. Don't retry; auth errors are not transient.

from onehouse_python_sdk.resources import AuthError

try:
client = OnehouseResources()
client.show_clusters()
except AuthError as e:
print(f"Auth failed: {e}")
raise SystemExit(1)

SqlParseError

The server rejected the SQL at parse time (HTTP 400 with the error text in the grpc-message header). Common causes: a misspelled keyword, missing required clause, or a feature the server doesn't support on your project.

Handle by: fixing the SQL. If you got here from a typed helper, file a bug — typed helpers should produce parseable SQL. If you got here from client.execute(...) with hand-written SQL, the message tells you what the parser disliked.

from onehouse_python_sdk.resources import SqlParseError

try:
client.execute("CREAT CLUSTER `prod` TYPE = 'Managed'") # typo
except SqlParseError as e:
print(f"SQL invalid: {e}")
# → "syntax error at or near \"CREAT\""

OperationFailedError

The operation reached a terminal failure status — FAILED (the server tried and could not complete the work) or INVALID (the server marked the request invalid post-submission).

Attributes: request_id, api_status, api_response — the server's failure payload usually has a human-readable reason.

Handle by: logging api_response and surfacing the reason. Retrying may help for transient failures (cluster busy, network hiccup downstream) but won't help for permanent ones (name already exists, quota exceeded).

from onehouse_python_sdk.resources import OperationFailedError

try:
client.create_cluster("prod", type="Managed", max_ocu=10, min_ocu=1)
except OperationFailedError as e:
print(f"Operation {e.request_id} failed with {e.api_status}")
print(f"Server said: {e.api_response}")

OperationTimeoutError

The client gave up polling before the operation reached a terminal status. The operation is still running on the server. Re-submitting (rather than resuming) creates a duplicate resource — see below for the right pattern.

Attributes: request_id, timeout, last_status (the most recent non-terminal status the poller saw).

Handle by: resuming with get_status(request_id). If you consistently hit this, raise the default timeout on the client.

Resuming a timed-out operation

OperationTimeoutError.request_id is still valid server-side. Resume with get_status instead of re-submitting.

import time
from onehouse_python_sdk.resources import ApiStatus, OperationTimeoutError

try:
result = client.create_cluster(
"prod", type="Managed", max_ocu=10, min_ocu=1, timeout=30,
)
except OperationTimeoutError as e:
# Operation still running on the server — keep polling.
status = client.get_status(e.request_id)
while not status.terminal:
time.sleep(5)
status = client.get_status(e.request_id)
print(status.api_status, status.api_response)

Tuning timeout and poll interval

Three knobs control how long blocking calls wait and how aggressively they poll.

KnobDefaultSet on the client (applies to every call)Override per call
Execute timeout60.0 sOnehouseResources(default_execute_timeout=300.0)client.create_cluster(..., timeout=300)
Poll interval1.0 s, grows to a cap of 10.0 s (1.5× backoff)OnehouseResources(default_poll_interval=2.0)client.create_cluster(..., poll_interval=2)
HTTP request timeout30.0 sOnehouseResources(timeout=60.0)not exposed per call

The HTTP request timeout is per network round-trip (submit or one poll). The execute timeout is the total time to reach a terminal status, including polls.

# Long-running operations (large catalogs, big cluster spin-ups)
client = OnehouseResources(
default_execute_timeout=600.0, # 10 minutes total
default_poll_interval=2.0, # poll every 2s instead of 1s
)

# One-off override for a specific call
client.create_cluster("prod", type="Managed", max_ocu=20, min_ocu=2, timeout=900)

HTTP errors not surfaced as named exceptions

  • 429 Too Many Requests — the transport retries with backoff (up to max_retries=3 by default, honoring Retry-After). You only see a ResourcesError if every retry is exhausted.
  • 5xx / other 4xx — raised as the base ResourcesError with the status code and response snippet in the message.
  • Network errors (DNS, connection reset, TLS) — retried max_retries times, then raised as ResourcesError.