Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / API reference

Reference

Errors

The JSON error body, the exception-to-code mapping, and every error code with its HTTP status, raise sites, and first message.

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

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13