Skip to main content

Error Handling

Governance decisions surface as Python exceptions. The SDK raises typed exceptions you can catch and handle in your agent code.

Import

from openbox_langgraph import (
GovernanceBlockedError,
GovernanceHaltError,
GuardrailsValidationError,
ApprovalRejectedError,
ApprovalExpiredError,
OpenBoxConfigError,
)

Governance Exception Hierarchy

ExceptionCauseDescription
GovernanceBlockedErrorBLOCK verdictOperation blocked by an OPA/Rego policy
GovernanceHaltErrorHALT verdictEntire agent session terminated by policy
GuardrailsValidationErrorGuardrails matchPII, toxic content, or restricted data detected
ApprovalRejectedErrorHITL rejectedA human rejected the approval request
ApprovalExpiredErrorHITL expiredNo human decision before the server-side approval deadline

All governance exceptions carry the human-readable decision message from OpenBox as the exception message (str(e)).

Handling Each Type

GovernanceBlockedError

Raised when a tool call or LLM invocation is blocked by policy. The graph execution stops at the blocked operation.

from openbox_langgraph import GovernanceBlockedError, create_openbox_graph_handler

governed = create_openbox_graph_handler(
graph=app,
api_url=os.getenv("OPENBOX_URL"),
api_key=os.getenv("OPENBOX_API_KEY"),
)

try:
result = await governed.ainvoke({"messages": [("user", input)]})
except GovernanceBlockedError as e:
logger.warning(f"Operation blocked by policy: {str(e)}")
# Return a safe fallback response
return {"response": "That action is not permitted."}

GovernanceHaltError

Raised when OpenBox issues a HALT verdict — the entire session is terminated, not just a single operation. Treat this as unrecoverable for the current session.

from openbox_langgraph import GovernanceHaltError

try:
result = await governed.ainvoke({"messages": [("user", input)]})
except GovernanceHaltError as e:
logger.error(f"Agent session halted: {str(e)}")
await notify_ops_team(str(e))
# Do not retry — start a new session if needed
raise

GuardrailsValidationError

Raised when a guardrail detects a policy violation — PII in a tool output, toxic content in an LLM response, or restricted data patterns.

from openbox_langgraph import GuardrailsValidationError

try:
result = await governed.ainvoke({"messages": [("user", input)]})
except GuardrailsValidationError as e:
logger.warning(f"Guardrail triggered: {str(e)}")
# Optionally inspect which guardrail fired
return {"response": "I can't process that content."}

ApprovalRejectedError

Raised when a human reviewer rejects the HITL approval request. The operation does not proceed.

from openbox_langgraph import ApprovalRejectedError

try:
result = await governed.ainvoke({"messages": [("user", input)]})
except ApprovalRejectedError as e:
logger.info(f"Human rejected approval: {str(e)}")
return {"response": f"Your request was reviewed and declined: {str(e)}"}

ApprovalExpiredError

Raised when no human decision is made before the server-side approval deadline.

from openbox_langgraph import ApprovalExpiredError

try:
result = await governed.ainvoke({"messages": [("user", input)]})
except ApprovalExpiredError as e:
logger.warning(f"Approval timed out: {str(e)}")
# Retry or escalate
return {"response": "Your request is pending review. Please try again later."}

Catching All Governance Errors

For a single catch-all handler, use the OpenBoxError base class:

from openbox_langgraph import OpenBoxError

try:
result = await governed.ainvoke({"messages": [("user", input)]})
except OpenBoxError as e:
logger.warning(f"Governance decision: {type(e).__name__}: {str(e)}")
return {"response": "This action was not permitted."}

Configuration Exceptions

These are raised during create_openbox_graph_handler() — at initialization time, not during graph execution. Handle them where you set up your handler.

ExceptionCause
OpenBoxErrorBase class for all SDK errors
OpenBoxAuthErrorInvalid or missing API key
OpenBoxConfigErrorInvalid SDK configuration, including incomplete agent DID identity values
OpenBoxNetworkErrorCannot reach OpenBox Core
OpenBoxInsecureURLErrorHTTP used for a non-localhost URL
from openbox_langgraph import (
OpenBoxAuthError,
OpenBoxConfigError,
OpenBoxNetworkError,
OpenBoxInsecureURLError,
)

try:
governed = create_openbox_graph_handler(
graph=app,
api_url=os.getenv("OPENBOX_URL"),
api_key=os.getenv("OPENBOX_API_KEY"),
agent_did=os.getenv("OPENBOX_AGENT_DID"),
agent_private_key=os.getenv("OPENBOX_AGENT_PRIVATE_KEY"),
)
except OpenBoxInsecureURLError:
raise RuntimeError("OPENBOX_URL must use HTTPS in production")
except OpenBoxConfigError as e:
raise RuntimeError(f"Invalid OpenBox SDK configuration: {e}")
except OpenBoxAuthError:
raise RuntimeError("Invalid OPENBOX_API_KEY — check your credentials")
except OpenBoxNetworkError as e:
raise RuntimeError(f"Cannot reach OpenBox Core: {e}")

OpenBox Returns 401 invalid token or agent identity

This usually means API-key authentication succeeded far enough to reach OpenBox, but the agent identity material did not match the registered agent.

What to verify:

  1. The API key belongs to the same OpenBox agent as OPENBOX_AGENT_DID.
  2. OPENBOX_AGENT_DID uses the did:aip:<uuid> format.
  3. OPENBOX_AGENT_PRIVATE_KEY is the base64 raw 32-byte Ed25519 seed returned by OpenBox, not a PEM key or public key.
  4. The DID private key has not been rotated since the runtime environment was configured.
  5. The runtime clock is synchronized so signature timestamp checks pass.

Best Practices

  1. Catch GovernanceHaltError separately — it signals session termination; do not retry the same session
  2. Log governance exceptions — the message comes from your policy and aids debugging
  3. Provide fallback responses — not every block should surface as an unhandled exception to the user
  4. Clean up resources on HALT — release connections and notify downstream systems before exiting
  5. Never catch and ignore — governance exceptions are intentional decisions; swallowing them defeats the purpose

Debugging

Enable verbose SDK logging to trace governance decisions:

OPENBOX_DEBUG=1 python agent.py

This logs the full event payload sent to OpenBox and the raw verdict received, which helps diagnose unexpected blocks or missing events.

Next Steps

  1. Configuration — Configure on_api_error, timeouts, and HITL behavior
  2. Event Model — Understand the semantic event types that trigger governance decisions
  3. Approvals and Guardrails — Review and process HITL requests in the dashboard