This page describes the error body that every failed API request returns and lists every error code found in code. It covers the 213 code constants in src/cluster_erp/core/errors.py, the codes that modules and server files define locally, and the codes that the exception handlers in src/cluster_erp/server/error_handler.py assign. The design of the error model is explained in Errors and exceptions.
Error body¶
Every handler builds the body with _build_body. From src/cluster_erp/server/error_handler.py:33-46:
def _build_body(
code: str,
message: str,
trace_id: str,
details: dict[str, Any] | None = None,
) -> dict[str, Any]:
body: dict[str, Any] = {
"code": code,
"message": message,
"trace_id": trace_id,
}
if details:
body["details"] = details
return {"error": body}
Illustrative response built from _build_body and the RESOURCE_NOT_FOUND factory not_found (src/cluster_erp/core/errors.py:357-367):
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Warehouse (<warehouse_id>) not found",
"trace_id": "<yymmdd>-<seq>",
"details": {"resource": "Warehouse", "id": "<warehouse_id>"}
}
}
| Field | Meaning |
|---|---|
code |
Machine-readable code. Clients map it to a localised message (src/cluster_erp/core/errors.py:6-16). |
message |
Developer-oriented English text (src/cluster_erp/core/errors.py:10-11). |
trace_id |
Date prefix yymmdd and a daily sequence from the trace_sequence table; a time-based fallback when the table write fails (src/cluster_erp/server/error_handler.py:49-70). |
details |
Present only when not empty (src/cluster_erp/server/error_handler.py:45-46). |
debug_message, exception_type |
Added for unhandled and mapped exceptions only when debug is on (src/cluster_erp/server/error_handler.py:306-308, src/cluster_erp/server/error_handler.py:346-347). |
Exception handlers¶
install_error_handlers registers one handler per exception class (src/cluster_erp/server/error_handler.py:200-398). The table lists what each produces.
| Exception | Code | Status | Source |
|---|---|---|---|
AppError |
the error's code |
the error's status_code |
src/cluster_erp/server/error_handler.py:203-224 |
RequestValidationError |
VALIDATION_ERROR, details.fields lists field and reason |
422 |
src/cluster_erp/server/error_handler.py:226-246 |
HTTPException |
HTTP_<status>, or the code of a dict detail |
the exception's status | src/cluster_erp/server/error_handler.py:248-270 |
SQLAlchemy DisconnectionError |
DB_CONNECTION_ERROR |
503 |
src/cluster_erp/server/error_handler.py:128 |
SQLAlchemy TimeoutError |
DB_TIMEOUT |
503 |
src/cluster_erp/server/error_handler.py:131 |
SQLAlchemy IntegrityError |
DB_UNIQUE_VIOLATION with details.constraint for a unique violation, else DB_INTEGRITY_ERROR |
409 |
src/cluster_erp/server/error_handler.py:132, src/cluster_erp/server/error_handler.py:355-392 |
SQLAlchemy DataError |
VALIDATION_ERROR |
422 |
src/cluster_erp/server/error_handler.py:136 |
SQLAlchemy OperationalError |
DB_OPERATIONAL_ERROR |
500 |
src/cluster_erp/server/error_handler.py:136 |
decimal.InvalidOperation |
VALIDATION_ERROR |
422 |
src/cluster_erp/server/error_handler.py:146-148 |
Pydantic ValidationError |
SERIALIZATION_ERROR |
500 |
src/cluster_erp/server/error_handler.py:150-155 |
TimeoutError, TypeError, ValueError |
TIMEOUT_ERROR, TYPE_ERROR, VALUE_ERROR |
504, 500, 500 |
src/cluster_erp/server/error_handler.py:157-163 |
any other Exception |
INTERNAL_ERROR, or the mapped code when the class is in _EXCEPTION_MAP; Retry-After: 2 when the status is 503 |
500 |
src/cluster_erp/server/error_handler.py:272-310 |
Codes common to every route¶
Authentication, permission, and entitlement checks run before a handler and can answer any route. The entitlement gate raises HTTPException with a dict detail, so the code reaches the body as code (src/cluster_erp/server/entitlements.py:233-257, src/cluster_erp/server/error_handler.py:252-262).
| Code | Status | Cause | Source |
|---|---|---|---|
AUTH_MISSING_HEADER |
401 |
No Bearer header and no access cookie. | src/cluster_erp/modules/auth/guards.py:101-105 |
AUTH_CSRF_HEADER_MISSING |
403 |
A cookie-authenticated write without the CSRF header. | src/cluster_erp/modules/auth/session_cookies.py:112-121 |
AUTH_TOKEN_EXPIRED |
401 |
The access token has expired. | src/cluster_erp/modules/auth/guards.py:112-113 |
AUTH_TOKEN_INVALID |
401 |
The token does not verify, has no jti, or was issued before the user's last sign-out. |
src/cluster_erp/modules/auth/guards.py:114-115, src/cluster_erp/modules/auth/guards.py:127, src/cluster_erp/modules/auth/guards.py:163-165 |
AUTH_TOKEN_PAYLOAD_INVALID |
401 |
The sub claim is missing or not a UUID. |
src/cluster_erp/modules/auth/guards.py:117-122 |
AUTH_TOKEN_BLACKLISTED |
401 |
The token's jti is in the blacklist. |
src/cluster_erp/modules/auth/guards.py:130-131 |
AUTH_USER_INACTIVE |
401 |
The user is missing, deactivated, or inactive. | src/cluster_erp/modules/auth/guards.py:150-151 |
AUTH_TENANT_UNRESOLVED |
401 |
Multi-tenant mode: no active tenant could be resolved for the request. | src/cluster_erp/server/dependencies.py:143-148 |
AUTH_PERMISSION_DENIED |
403 |
The caller lacks a required permission code, or an ABAC policy denies it; details.required_permission names the code. |
src/cluster_erp/core/errors.py:388-394 |
entitlement.no_seat |
403 |
The user holds no seat on an app that covers the module. | src/cluster_erp/server/entitlements.py:236-240 |
entitlement.module_not_installed |
403 |
No installed app covers the module. | src/cluster_erp/server/entitlements.py:241-245 |
entitlement.app_not_licensed |
403 |
The covering app is not licensed. | src/cluster_erp/server/entitlements.py:246-250 |
entitlement.license_read_only |
403 |
The licence is in its read-only state and the method is not GET, HEAD, or OPTIONS. |
src/cluster_erp/server/entitlements.py:253-257 |
VALIDATION_ERROR |
422 |
The request does not match the route's parameters or schema. | src/cluster_erp/server/error_handler.py:226-246 |
Codes defined in the core error module¶
One table per comment section of src/cluster_erp/core/errors.py. Status, raise-site count, and first site come from a syntax-tree scan of src/cluster_erp/ for AppError(...), the factories in core/errors.py, and HTTPException(...). The message column quotes the English message at the first site; it is the cause as the code states it. A code with no raise site is defined but not raised by any scanned call. Codes the exception handlers assign show the handler instead.
Codes of the business sections of the same file (CRM to procurement) are listed on Business error codes.
Auth¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
AUTH_MISSING_HEADER |
src/cluster_erp/core/errors.py:34 |
401 |
1 | src/cluster_erp/modules/auth/guards.py:103 |
Missing or invalid authorization header |
AUTH_TOKEN_EXPIRED |
src/cluster_erp/core/errors.py:35 |
401 |
1 | src/cluster_erp/modules/auth/guards.py:113 |
Token expired |
AUTH_TOKEN_INVALID |
src/cluster_erp/core/errors.py:36 |
401 |
3 | src/cluster_erp/modules/auth/guards.py:115 |
Invalid token |
AUTH_TOKEN_PAYLOAD_INVALID |
src/cluster_erp/core/errors.py:37 |
401 |
1 | src/cluster_erp/modules/auth/guards.py:120 |
Invalid token payload |
AUTH_INVALID_CREDENTIALS |
src/cluster_erp/core/errors.py:38 |
401 |
3 | src/cluster_erp/modules/auth/login/routes.py:143 |
Account locked. Try again in {remaining // 60 + 1} minutes. |
AUTH_REFRESH_FAILED |
src/cluster_erp/core/errors.py:39 |
401 |
4 | src/cluster_erp/modules/auth/login/routes.py:269 |
Missing refresh token |
AUTH_CSRF_HEADER_MISSING |
src/cluster_erp/core/errors.py:40 |
403 |
2 | src/cluster_erp/modules/auth/login/routes.py:271 |
cookie-authenticated requests must carry X-Requested-With |
AUTH_USER_INACTIVE |
src/cluster_erp/core/errors.py:41 |
401 |
2 | src/cluster_erp/modules/auth/guards.py:151 |
User not found or inactive |
AUTH_PERMISSION_DENIED |
src/cluster_erp/core/errors.py:42 |
403 |
16 | src/cluster_erp/modules/auth/approvals/services.py:364 |
This approval step requires the '{step.role_code}' role. |
AUTH_TOKEN_BLACKLISTED |
src/cluster_erp/core/errors.py:43 |
401 |
1 | src/cluster_erp/modules/auth/guards.py:131 |
Token has been revoked |
AUTH_TENANT_UNRESOLVED |
src/cluster_erp/core/errors.py:47 |
401 |
1 | src/cluster_erp/server/dependencies.py:144 |
Unable to resolve an active tenant for this request |
AUTH_PRIVILEGE_ESCALATION |
src/cluster_erp/core/errors.py:48 |
403 |
3 | src/cluster_erp/modules/auth/roles/services.py:163 |
- |
Resource CRUD¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
RESOURCE_NOT_FOUND |
src/cluster_erp/core/errors.py:51 |
404, 422 |
695 | src/cluster_erp/core/tenant_fields.py:1214 |
- |
RESOURCE_CONFLICT |
src/cluster_erp/core/errors.py:52 |
400, 409 |
28 | src/cluster_erp/modules/auth/roles/routes.py:63 |
- |
RESOURCE_PROTECTED |
src/cluster_erp/core/errors.py:53 |
400 |
1 | src/cluster_erp/modules/auth/roles/routes.py:106 |
- |
Validation¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
VALIDATION_ERROR |
src/cluster_erp/core/errors.py:56 |
400, 404, 409, 422, 500, 502 |
79 | src/cluster_erp/core/product_breakdown.py:105 |
A model/version cannot be set without a product |
PARTY_ASSIGNMENT_INCOMPLETE |
src/cluster_erp/core/errors.py:60 |
422 |
1 | src/cluster_erp/server/routes/party_composite.py:134 |
assignment needs organisation, position, employment type and start date |
FIELD_DEFINITION_INVALID |
src/cluster_erp/core/errors.py:65 |
409, 422 |
27 | src/cluster_erp/core/tenant_fields.py:974 |
unknown bucket {bucket} for {spec.entity_type} |
FIELD_VALUE_INVALID |
src/cluster_erp/core/errors.py:66 |
422 |
4 | src/cluster_erp/core/tenant_fields.py:846 |
{defn.key}: {message} |
FIELD_DEFINITION_DUPLICATE |
src/cluster_erp/core/errors.py:70 |
409 |
1 | src/cluster_erp/core/tenant_fields.py:1140 |
{why}: {existing.code} ({label}) |
MRP_ITEM_OUTSIDE_OPENING_PERIOD |
src/cluster_erp/core/errors.py:71 |
422 |
1 | src/cluster_erp/modules/procurement/mrp/routes.py:399 |
Planned order lies outside the opening period |
DELIVERY_ADDRESS_INVALID |
src/cluster_erp/core/errors.py:75 |
422 |
5 | src/cluster_erp/modules/procurement/delivery_address.py:97 |
A delivery organisation is required when a delivery address is set |
Business logic¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
UNIT_CONVERSION_FAILED |
src/cluster_erp/core/errors.py:78 |
400 |
1 | src/cluster_erp/modules/products_and_services/settings/routes.py:214 |
Cannot convert between these units |
APPROVAL_INVALID_STATE |
src/cluster_erp/core/errors.py:79 |
400, 409, 422 |
8 | src/cluster_erp/modules/auth/approval_chains/services.py:132 |
This approval chain has in-flight requests; finish or cancel them before changing its step... |
DOCUMENT_VERSION_INVALID_STATUS |
src/cluster_erp/core/errors.py:80 |
400, 404 |
6 | src/cluster_erp/modules/documents/documents/services.py:230 |
Only draft versions can be edited |
DOCUMENT_VERSION_ALREADY_ACTIVE |
src/cluster_erp/core/errors.py:81 |
- | 0 | - | - |
DOCUMENT_NO_ATTACHMENT |
src/cluster_erp/core/errors.py:82 |
400 |
1 | src/cluster_erp/modules/documents/documents/services.py:270 |
Version must have an attachment before activation |
Currency¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
TCMB_SYNC_FAILED |
src/cluster_erp/core/errors.py:85 |
- | 0 | - | - |
Concurrency¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
CONCURRENCY_VERSION_CONFLICT |
src/cluster_erp/core/errors.py:324 |
409 |
60 | src/cluster_erp/core/tenant_fields.py:1216 |
- |
External services¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
SMTP_NOT_CONFIGURED |
src/cluster_erp/core/errors.py:327 |
503 |
2 | src/cluster_erp/server/routes/email.py:134 |
SMTP is not configured. Set CLUSTER_ERP_SMTP_* in .env |
SMTP_ERROR |
src/cluster_erp/core/errors.py:328 |
502 |
2 | src/cluster_erp/server/routes/email.py:184 |
SMTP error: {exc} |
FILE_NOT_FOUND_ON_DISK |
src/cluster_erp/core/errors.py:329 |
404 |
1 | src/cluster_erp/server/routes/attachments.py:115 |
File not found on disk |
Database¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
DB_CONNECTION_ERROR |
src/cluster_erp/core/errors.py:332 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
DB_TIMEOUT |
src/cluster_erp/core/errors.py:333 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
DB_INTEGRITY_ERROR |
src/cluster_erp/core/errors.py:334 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
DB_UNIQUE_VIOLATION |
src/cluster_erp/core/errors.py:339 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
DB_OPERATIONAL_ERROR |
src/cluster_erp/core/errors.py:340 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
Serialization¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
SERIALIZATION_ERROR |
src/cluster_erp/core/errors.py:343 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
Runtime¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
TYPE_ERROR |
src/cluster_erp/core/errors.py:346 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
VALUE_ERROR |
src/cluster_erp/core/errors.py:347 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
TIMEOUT_ERROR |
src/cluster_erp/core/errors.py:348 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
Server¶
| Code | Defined at | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
INTERNAL_ERROR |
src/cluster_erp/core/errors.py:351 |
see Exception handlers | 0 | src/cluster_erp/server/error_handler.py |
- |
Codes defined outside the core error module¶
These codes are string constants or literals in a module or server file. Rows are grouped by the folder of the first raise site.
| Area | Code | HTTP status | Raise sites | First site | Message at first site |
|---|---|---|---|---|---|
auth |
ABAC_RESOURCE_RULES_UNSUPPORTED |
422 |
1 | src/cluster_erp/modules/auth/policies/services.py:81 |
Permission '{permission_code}' does not enforce resource.* rules at the object level yet —... |
auth |
AUTH_TOO_MANY_ATTEMPTS |
429 |
1 | src/cluster_erp/modules/auth/login/routes.py:93 |
Too many sign-in attempts; try again shortly |
calendars |
CALENDAR_BASE_CYCLE |
400 |
1 | src/cluster_erp/modules/calendars/services.py:373 |
Base calendar would create an inheritance cycle. |
calendars |
CALENDAR_INVALID_EXCEPTION |
400 |
4 | src/cluster_erp/modules/calendars/services.py:447 |
date_to must be on or after date_from. |
core |
CURRENCY_UNKNOWN |
422 |
1 | src/cluster_erp/core/currencies.py:44 |
Currency {code} is not an active currency in the reference data |
core |
UPLOAD_TYPE_REFUSED |
415 |
2 | src/cluster_erp/core/upload_policy.py:70 |
file type '.{ext or '?'}' is not accepted |
fleet |
FLEET_ASSET_NOT_OPERABLE |
400 |
1 | src/cluster_erp/modules/fleet/meters/services.py:260 |
readings cannot be recorded for a deactivated or disposed asset |
fleet |
FLEET_INVALID_CAMPAIGN |
400 |
10 | src/cluster_erp/modules/fleet/tco/services.py:606 |
a closed campaign is a sealed record |
fleet |
FLEET_INVALID_COMPLIANCE |
400 |
8 | src/cluster_erp/modules/fleet/fuel/services.py:466 |
a completed obligation is a sealed record |
fleet |
FLEET_INVALID_FUEL |
400 |
5 | src/cluster_erp/modules/fleet/fuel/services.py:130 |
fuel quantity must be positive |
fleet |
FLEET_INVALID_METER |
400 |
1 | src/cluster_erp/modules/fleet/meters/services.py:125 |
meter kind already exists on this asset |
fleet |
FLEET_INVALID_OPERATION |
400 |
36 | src/cluster_erp/modules/fleet/operations/services.py:74 |
the referenced party does not exist |
fleet |
FLEET_INVALID_PLAN |
400 |
7 | src/cluster_erp/modules/fleet/maintenance/services.py:102 |
a plan targets exactly one of: a single asset OR a class |
fleet |
FLEET_INVALID_REMINDER |
400 |
4 | src/cluster_erp/modules/fleet/maintenance/services.py:343 |
snooze needs a future date |
fleet |
FLEET_INVALID_STATUS |
400 |
3 | src/cluster_erp/modules/fleet/assets/services.py:283 |
unknown action {action} |
fleet |
FLEET_INVALID_TIRE |
400 |
8 | src/cluster_erp/modules/fleet/tco/services.py:428 |
a scrapped tire is a sealed record |
fleet |
FLEET_INVALID_WO |
400 |
19 | src/cluster_erp/modules/fleet/workorders/services.py:125 |
a {row.status} work order is closed to editing |
fleet |
FLEET_READING_CHAIN_BLOCKED |
400 |
1 | src/cluster_erp/modules/fleet/meters/services.py:524 |
a later reading depends on this document — mark the dependent reading incorrect first, the... |
fleet |
FLEET_READING_INVALID |
400 |
9 | src/cluster_erp/modules/fleet/meters/services.py:266 |
reading date is in the future |
fleet |
FLEET_READING_OUT_OF_ORDER |
400 |
1 | src/cluster_erp/modules/fleet/meters/services.py:280 |
reading date must be after the meter's latest reading |
intranet |
INTRANET_ERROR |
400 |
5 | src/cluster_erp/modules/intranet/content/services.py:1098 |
content has not been published — nothing to acknowledge |
intranet |
INTRANET_EVENT_ENDS_BEFORE_START |
422 |
1 | src/cluster_erp/modules/intranet/events/services.py:377 |
ends_at must not be before starts_at |
intranet |
INTRANET_EVENT_FULL |
422 |
1 | src/cluster_erp/modules/intranet/events/lifecycle.py:255 |
the event is full and has no waiting list |
intranet |
INTRANET_EVENT_INVALID_STATUS |
422 |
5 | src/cluster_erp/modules/intranet/events/lifecycle.py:108 |
unknown event action {action} |
intranet |
INTRANET_EVENT_NOT_EDITABLE |
422 |
1 | src/cluster_erp/modules/intranet/events/lifecycle.py:139 |
an event in status {current} cannot be edited; restore or uncancel it first |
intranet |
INTRANET_EVENT_REASON_REQUIRED |
422 |
1 | src/cluster_erp/modules/intranet/events/lifecycle.py:306 |
a cancellation needs a reason the invitees can read |
intranet |
INTRANET_EVENT_RSVP_CLOSED |
422 |
1 | src/cluster_erp/modules/intranet/events/services.py:609 |
this event does not take answers (not published or RSVP is off) |
intranet |
INTRANET_EVENT_UNCANCEL_EXPIRED |
422 |
1 | src/cluster_erp/modules/intranet/events/lifecycle.py:156 |
the cancellation can be taken back within 24 hours only |
intranet |
INTRANET_INVALID_STATUS |
400 |
9 | src/cluster_erp/modules/intranet/content/services.py:821 |
unknown action {action} |
intranet |
INTRANET_LEAVE_DOCUMENT_REQUIRED |
409 |
1 | src/cluster_erp/modules/intranet/leave/services.py:515 |
leave type {lt.code} requires a supporting document before submission |
intranet |
INTRANET_LEAVE_GENDER_RESTRICTED |
409 |
1 | src/cluster_erp/modules/intranet/leave/services.py:459 |
leave type {lt.code} is restricted to gender {restriction}; the party's recorded gender is... |
intranet |
INTRANET_LEAVE_INSUFFICIENT_BALANCE |
409 |
1 | src/cluster_erp/modules/intranet/leave/services.py:539 |
requested {days} day(s) in {year} but only {available} available (granted + carried over −... |
intranet |
INTRANET_LEAVE_NOTICE_TOO_SHORT |
409 |
1 | src/cluster_erp/modules/intranet/leave/services.py:490 |
leave type {lt.code} requires {lt.min_notice_days} days notice; the start date is {notice}... |
intranet |
INTRANET_LEAVE_OVERLAP |
409 |
1 | src/cluster_erp/modules/intranet/leave/services.py:432 |
the range overlaps open leave request {clash} |
intranet |
INTRANET_MEDIA_ENTITY |
422 |
1 | src/cluster_erp/modules/intranet/events/routes.py:317 |
media can hang on {', '.join(_MEDIA_ENTITY_TYPES)} only |
intranet |
INTRANET_NO_PARTY |
400 |
5 | src/cluster_erp/modules/intranet/content/routes.py:384 |
no party to acknowledge as — pass party_id or sign in as an employee |
intranet |
INTRANET_PARTY_NOT_EMPLOYEE |
422 |
1 | src/cluster_erp/modules/intranet/access.py:139 |
the named party does not exist or is not an active employee |
privacy |
PRIVACY_CONFIRMATION_MISMATCH |
400 |
1 | src/cluster_erp/modules/privacy/requests/services.py:308 |
the typed confirmation does not match the request code |
privacy |
PRIVACY_EXPORT_MISSING |
404 |
2 | src/cluster_erp/modules/privacy/requests/services.py:279 |
subject request {row.code} has no prepared data file |
privacy |
PRIVACY_INVALID_STATE |
409 |
1 | src/cluster_erp/modules/privacy/requests/services.py:86 |
subject request {request.code} cannot '{step}' while {request.status} (type {request.reque... |
procurement |
INFO_RECORD_EXISTS |
409 |
1 | src/cluster_erp/modules/procurement/info_records/services.py:169 |
An info record already exists for this vendor and product |
sales_orders |
SALES_DELIVERY_NON_WORKING |
422 |
1 | src/cluster_erp/modules/sales_orders/orders/services.py:108 |
Delivery date is a non-working day on the customer's calendar. |
server |
SIGNUP_CLOSED |
403 |
1 | src/cluster_erp/server/signup.py:230 |
this node does not accept new memberships |
server |
SIGNUP_DOMAIN_TAKEN |
409 |
1 | src/cluster_erp/server/signup.py:303 |
this mail domain already has a membership |
server |
SIGNUP_INVALID |
402, 422, 500 |
6 | src/cluster_erp/server/signup.py:166 |
signup mail transport is not configured |
server |
SIGNUP_NOTICE_MISSING |
409 |
1 | src/cluster_erp/server/signup.py:242 |
this node has no privacy notice configured; signups are closed |
server |
SIGNUP_NOTICE_REQUIRED |
422 |
1 | src/cluster_erp/server/signup.py:248 |
the current privacy notice must be accepted |
server/routes |
CONSOLE_BACKUP_DISABLED |
403 |
1 | src/cluster_erp/server/routes/vendor.py:697 |
on-demand backups from the console are disabled on this node |
server/routes |
SIGNUP_ALREADY_PROVISIONED |
409 |
1 | src/cluster_erp/server/routes/signup.py:215 |
this membership is already provisioned; contact the vendor |
server/routes |
SIGNUP_RATE_LIMITED |
429 |
1 | src/cluster_erp/server/routes/signup.py:81 |
too many signup attempts; try again later |
server/routes |
SIGNUP_VERIFY_EXPIRED |
410 |
1 | src/cluster_erp/server/routes/signup.py:184 |
this verification link has expired; request a fresh one from the signup page |
server/routes |
VENDOR_ACCOUNT_LOCKED |
401 |
1 | src/cluster_erp/server/routes/vendor.py:124 |
Too many attempts; try later |
server/routes |
VENDOR_AUTH_REQUIRED |
401, 403 |
5 | src/cluster_erp/server/routes/vendor.py:53 |
Operator console unavailable |
server/routes |
VENDOR_INVALID_CREDENTIALS |
401 |
1 | src/cluster_erp/server/routes/vendor.py:134 |
Invalid credentials |
server/routes |
VENDOR_TOTP_NOT_ENROLLED |
403 |
1 | src/cluster_erp/server/routes/vendor.py:128 |
Operator has no authenticator enrolled (operators totp-enroll) |
server/routes |
VENDOR_TOTP_REQUIRED |
401 |
1 | src/cluster_erp/server/routes/vendor.py:126 |
Authenticator code required |
Generic HTTP codes¶
An HTTPException with a plain detail becomes HTTP_<status> (src/cluster_erp/server/error_handler.py:251).
| Code | Raise sites | First site |
|---|---|---|
HTTP_404 |
7 | src/cluster_erp/modules/app_store/routes.py:136 |
HTTP_409 |
3 | src/cluster_erp/modules/app_store/services.py:179 |
HTTP_422 |
4 | src/cluster_erp/modules/app_store/services.py:243 |
Related pages¶
Verified against commit 92f8051 on 2026-11-13.