Errors¶
Every non-2xx response raises an APIError subclass mapped from the
HTTP status code, parsed from the Campus error envelope (with a
legacy error_code fallback). Auth endpoints normalize to the same
hierarchy, and the OAuth protocol error survives even when the server
strips envelope details in production — it is recovered from the
AUTH_* error code and readable via .oauth_error.
from campus_python import errors
try:
campus.api.circles.new(name="dup", tag="project")
except errors.ValidationError as err: # 422
for field_err in err.get_errors_for_field("name"):
print(field_err.code, field_err.message) # e.g. CONFLICT / name collision
except errors.ConflictError as err: # 409
print(err.request_id)
except errors.AuthenticationError as err: # 401
if err.oauth_error == "invalid_grant":
... # session gone — re-login
Common attributes on every error: status_code, error (machine
code), error_description, request_id (from the envelope, for
tracing), details, and notes (client-side headers/body for
debugging).
Naming note: the 501 class is this package's
errors.NotImplementedError — when catching broadly, qualify it
(errors.NotImplementedError) so it doesn't shadow the builtin.
| Status | Class | Typical cause |
|---|---|---|
| 400 | BadRequestError |
malformed request |
| 401 | AuthenticationError |
missing/expired credentials, OAuth errors (oauth_error) |
| 403 | AccessDeniedError |
insufficient permission; broker missing_scopes in details |
| 404 | NotFoundError |
unknown id/slug/vault key |
| 409 | ConflictError |
name collisions, duplicate resources |
| 422 | ValidationError |
validation failure — inspect .field_errors |
| 429 | RateLimitExceededError |
rate limited (traces ingest trips the client breaker first) |
| 500 | ServerError |
server-side failure (device-flow polls retry these) |
| 501 | errors.NotImplementedError |
endpoint stubbed upstream |
| 502 | MalformedResponseError |
upstream returned garbage |
| 503 | UnavailableError |
service down |
campus_python.errors
¶
campus.python.errors
Common error types used across Campus API for Python.
Reference: - campus/api/docs/api-error-spec.md - campus/auth/docs/auth-error-spec.md
FieldError
dataclass
¶
A single field validation error.
Attributes:
| Name | Type | Description |
|---|---|---|
field |
str
|
The field name that failed validation |
code |
str
|
Machine-readable error code (e.g., "INVALID_FORMAT", "MISSING") |
message |
str
|
Human-readable error message |
Source code in campus_python/errors.py
14 15 16 17 18 19 20 21 22 23 24 25 | |
APIError
¶
Bases: Exception
Base exception for all campus client errors.
Attributes:
| Name | Type | Description |
|---|---|---|
status_code |
int
|
HTTP status code |
error |
str
|
Error code (e.g., "NOT_FOUND", "VALIDATION_FAILED") |
error_description |
str | None
|
Human-readable error message |
error_uri |
str | None
|
Optional URI to error documentation |
request_id |
str | None
|
Request identifier for tracing (from error envelope) |
details |
dict[str, Any]
|
Additional error details from server |
errors |
list[FieldError] | None
|
Field-level validation errors (for ValidationError) |
notes |
dict[str, Any] | None
|
Client-side metadata (headers, body) |
Source code in campus_python/errors.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
status_code: int
instance-attribute
¶
error: str
instance-attribute
¶
error_description: str | None = error_description
instance-attribute
¶
error_uri: str | None = error_uri
instance-attribute
¶
request_id: str | None = request_id
instance-attribute
¶
details: dict[str, Any] = details or {}
instance-attribute
¶
errors: list[FieldError] | None = errors
instance-attribute
¶
notes: dict[str, Any] | None = notes
instance-attribute
¶
oauth_error: str | None
property
¶
Get the OAuth protocol error string from details.
Returns the OAuth error string (e.g., "invalid_client", "invalid_grant") if present in the error details. Used for auth endpoint errors.
Reference: campus/auth/docs/auth-error-spec.md
oauth_error_description: str | None
property
¶
Get the OAuth error description from details.
Returns the OAuth-specific error description if present.
__init__(status_code: int | None = None, error_description: str | None = None, *, error_uri: str | None = None, request_id: str | None = None, details: dict[str, Any] | None = None, errors: list[FieldError] | None = None, notes: dict[str, Any] | None = None)
¶
Source code in campus_python/errors.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | |
__init_subclass__() -> None
¶
Automatically register subclasses in the _registered_errors dict.
This enables mapping from HTTP status codes to specific error classes for automatic error handling based on status code.
Source code in campus_python/errors.py
121 122 123 124 125 126 | |
with_status_code(status_code: int, response_data: dict[str, Any] | str | None = None, *, error: str | None = None, error_description: str | None = None, error_uri: str | None = None, request_id: str | None = None, details: dict[str, Any] | None = None, errors: list[FieldError] | None = None) -> APIError | None
classmethod
¶
Create an APIError instance based on status code.
Handles both the new error envelope format and legacy format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
status_code
|
int
|
HTTP status code |
required |
response_data
|
dict[str, Any] | str | None
|
Full response data (dict or string) for parsing |
None
|
error
|
str | None
|
Explicit error code (overrides parsed value) |
None
|
error_description
|
str | None
|
Explicit error description (overrides parsed value) |
None
|
error_uri
|
str | None
|
Optional URI to error documentation |
None
|
request_id
|
str | None
|
Request identifier for tracing |
None
|
details
|
dict[str, Any] | None
|
Additional error details |
None
|
errors
|
list[FieldError] | None
|
Field-level validation errors |
None
|
Returns:
| Type | Description |
|---|---|
APIError | None
|
An APIError subclass instance or None if status_code < 400 |
Source code in campus_python/errors.py
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
BadRequestError
¶
Bases: APIError
Raised when request validation fails.
Source code in campus_python/errors.py
247 248 249 250 | |
AuthenticationError
¶
Bases: APIError
Raised when client is unauthenticated or authentication fails.
Source code in campus_python/errors.py
253 254 255 256 | |
AccessDeniedError
¶
Bases: APIError
Raised when an authenticated client lacks required permissions.
Source code in campus_python/errors.py
259 260 261 262 | |
NotFoundError
¶
Bases: APIError
Raised when a requested resource is not found.
Source code in campus_python/errors.py
265 266 267 268 | |
ConflictError
¶
Bases: APIError
Raised when a conflict occurs.
Source code in campus_python/errors.py
271 272 273 274 | |
ValidationError
¶
Bases: APIError
Raised when request validation fails with field-level details.
This error is returned with status code 422 and includes structured field-level error information.
Reference: campus/api/docs/api-error-spec.md Section 5
Source code in campus_python/errors.py
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | |
status_code = 422
class-attribute
instance-attribute
¶
error = 'VALIDATION_FAILED'
class-attribute
instance-attribute
¶
field_errors: list[FieldError]
property
¶
Return list of field errors, empty if none.
get_errors_for_field(field_name: str) -> list[FieldError]
¶
Get all errors for a specific field.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field_name
|
str
|
The field name to get errors for |
required |
Returns:
| Type | Description |
|---|---|
list[FieldError]
|
List of FieldError objects for the specified field |
Source code in campus_python/errors.py
293 294 295 296 297 298 299 300 301 302 | |
RateLimitExceededError
¶
Bases: APIError
Raised when rate limits are exceeded.
Source code in campus_python/errors.py
305 306 307 308 | |
ServerError
¶
Bases: APIError
Raised when the server encounters an error.
Source code in campus_python/errors.py
311 312 313 314 | |
NotImplementedError
¶
Bases: APIError
Raised when the endpoint is not yet implemented.
Source code in campus_python/errors.py
317 318 319 320 | |
MalformedResponseError
¶
Bases: APIError
Raised when the API response is malformed.
Source code in campus_python/errors.py
323 324 325 326 | |
UnavailableError
¶
Bases: APIError
Raised when the service is unavailable.
Source code in campus_python/errors.py
329 330 331 332 | |
oauth_error_from_code(code: str) -> str | None
¶
Recover the OAuth error code from a Campus envelope AUTH_* code.
The auth server strips the error envelope's details in production
(campus/common/errors/handlers.py), taking details.oauth_error with
it; the code remains and encodes the OAuth error as
AUTH_
Returns None for codes that carry no OAuth error (plain API codes).
Source code in campus_python/errors.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | |