Skip to content

Error Codes

OJS uses a structured error format for all error responses. Every error includes a machine-readable code, a human-readable message, and a flag indicating whether the client should retry.

{
"error": {
"code": "invalid_request",
"message": "Job envelope validation failed: 'type' is required",
"retryable": false,
"details": { },
"request_id": "req_019414d4-0002-7000-a000-000000000001"
}
}
FieldTypeRequiredDescription
codestringYesMachine-readable error code from the table below
messagestringYesHuman-readable description
retryablebooleanYesWhether the client should retry the request
detailsobjectNoAdditional structured information
request_idstringNoCorrelation ID for debugging

HTTP Status: 400 Bad Request Retryable: No

The request body is malformed JSON or is missing required fields.

Common causes:

  • Invalid JSON syntax
  • Missing type or args field in enqueue request
  • Missing job_id in ACK/FAIL request
  • Wrong Content-Type header

Example:

{
"error": {
"code": "invalid_request",
"message": "Missing required field: 'type'",
"retryable": false
}
}

Fix: Check that your request body is valid JSON and includes all required fields.


HTTP Status: 400 Bad Request Retryable: No

The job envelope is syntactically valid JSON but fails structural validation.

Common causes:

  • Invalid job type format (must be dot-namespaced, lowercase)
  • Invalid queue name (must match [a-z0-9][a-z0-9\-\.]*)
  • args is not an array
  • args contains non-JSON-native types
  • Invalid UUIDv7 format for job ID
  • Timestamp missing timezone designator

Example:

{
"error": {
"code": "invalid_payload",
"message": "Job envelope validation failed",
"retryable": false,
"details": {
"validation_errors": [
{
"path": "$.type",
"message": "must match pattern [a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*"
}
]
}
}
}

Fix: Validate your job envelope against the JSON Schema before submitting.


HTTP Status: 400 Bad Request Retryable: No

The job’s args do not conform to the registered schema referenced by the schema field.

Example:

{
"error": {
"code": "schema_validation",
"message": "Args do not conform to schema urn:ojs:schema:email.send:v1",
"retryable": false,
"details": {
"schema_uri": "urn:ojs:schema:email.send:v1",
"validation_errors": [
{
"path": "$.args[0]",
"message": "expected string, got number"
}
]
}
}
}

Fix: Check that your args match the schema definition. Use the schemas endpoint to inspect the registered schema.


HTTP Status: 404 Not Found Retryable: No

The requested job, queue, workflow, or other resource does not exist.

Example:

{
"error": {
"code": "not_found",
"message": "Job 019414d4-8b2e-7c3a-b5d1-f0e2a3b4c5d6 not found",
"retryable": false
}
}

Fix: Verify the resource ID is correct. Jobs in terminal states may be pruned after the retention period.


HTTP Status: 409 Conflict Retryable: No

A unique constraint was violated. Another job with the same uniqueness key already exists in the specified states.

Example:

{
"error": {
"code": "duplicate",
"message": "Unique constraint violated: existing job 019414d4-aaaa-7bbb-cccc-ddddeeee0001",
"retryable": false,
"details": {
"existing_job_id": "019414d4-aaaa-7bbb-cccc-ddddeeee0001",
"unique_key": "email.send:default:user@example.com"
}
}
}

Fix: This is expected behavior when using unique jobs with on_conflict: "reject". Either wait for the existing job to complete, use "ignore" to silently skip duplicates, or use "replace" to update the existing job.


HTTP Status: 409 Conflict Retryable: No

An invalid state transition was attempted.

Common causes:

  • ACK on a job that is not active (double-ack, or the job timed out and was reclaimed)
  • CANCEL on a job that is already completed or discarded
  • FAIL on a job that is not active

Example:

{
"error": {
"code": "conflict",
"message": "Job is in state 'completed', cannot transition to 'cancelled'",
"retryable": false,
"details": {
"current_state": "completed",
"requested_transition": "cancelled"
}
}
}

Fix: Check the job’s current state before performing operations. If you see this on ACK, the job’s visibility timeout may have expired and it was reclaimed by another worker. Consider increasing the visibility timeout or sending heartbeats more frequently.


HTTP Status: 503 Service Unavailable Retryable: Yes

The target queue is paused and not accepting new jobs or fetches.

Example:

{
"error": {
"code": "queue_paused",
"message": "Queue 'email' is paused",
"retryable": true
}
}

Fix: Wait for the queue to be resumed, or enqueue to a different queue.


HTTP Status: 429 Too Many Requests Retryable: Yes

The client has exceeded the rate limit. The response includes Retry-After and rate limit headers.

Example:

{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded: 100 requests per minute",
"retryable": true,
"details": {
"limit": 100,
"window": "1m",
"retry_after_seconds": 12
}
}
}

Fix: Back off and retry after the Retry-After period. Consider using batch enqueue to reduce request count.


HTTP Status: 503 Service Unavailable Retryable: Yes

The backend storage system (Redis, PostgreSQL, etc.) is unavailable or returned an error.

Example:

{
"error": {
"code": "backend_error",
"message": "Redis connection refused",
"retryable": true
}
}

Fix: This is usually transient. Retry with exponential backoff. Check the health endpoint for backend status.


HTTP Status: 504 Gateway Timeout Retryable: Yes

The operation did not complete within the allowed time.

Fix: Retry the request. If this happens consistently, check backend performance.


HTTP Status: 422 Unprocessable Entity Retryable: No

The requested feature is not supported by this server at its conformance level.

Example:

{
"error": {
"code": "unsupported",
"message": "Workflows require conformance level 3 or higher",
"retryable": false,
"details": {
"feature": "workflows",
"required_level": 3,
"server_level": 1
}
}
}

Fix: Check the conformance manifest to see what features the server supports.


HTTP Status: 413 Payload Too Large Retryable: No

The job envelope exceeds the server’s size limit (minimum 1 MiB).

Example:

{
"error": {
"code": "envelope_too_large",
"message": "Job envelope size 2.3 MiB exceeds maximum 1 MiB",
"retryable": false,
"details": {
"size_bytes": 2411724,
"max_bytes": 1048576
}
}
}

Fix: Reduce the size of your args. Store large data (files, images, documents) in external storage (S3, GCS) and pass references in args instead of inline data.

When a job handler fails, the error is reported as a structured object (separate from the API error format above). This is the error stored on the job envelope:

{
"type": "SmtpConnectionError",
"message": "Connection refused to smtp.example.com:587 after 30s timeout",
"backtrace": [
"at SmtpClient.connect (smtp.js:42:15)",
"at EmailSender.send (email_sender.js:18:22)",
"at handler (handlers/email.send.js:7:10)"
]
}
FieldTypeRequiredDescription
typestringYesError type or exception class name
messagestringYesHuman-readable description
backtracestring[]NoStack trace frames (max 50 frames recommended)

The type field is used to match against non_retryable_errors in the retry policy. If a failing job’s error type matches a non-retryable error, the job skips retry and moves directly to discarded.