Skip to content

Error Codes

Every error response uses a consistent envelope:

{
"success": false,
"message": "Human-readable description",
"code": "machine_readable_code",
"errors": []
}

The code field is stable. Use it for programmatic error handling. The message field may change between versions.


StatusMeaningRetryable?
400Bad request. Invalid inputNo
401Unauthorized. Missing or invalid API keyNo
403Forbidden. CSRF failure (session auth only)No
404Not found. Resource doesn’t existNo
409Conflict. Resource state prevents the actionNo (fix state first)
422Validation error. Request body/query failed schema validationNo (fix input)
429Rate limited. Too many requestsYes (after Retry-After seconds)
500Internal error. Unexpected server failureYes

These can occur on any endpoint.

CodeStatusDescription
bad_request400Request is malformed or semantically invalid.
unauthorized401No valid API key provided, or key has been revoked.
forbidden403Access denied.
not_found404Resource does not exist or URL does not match any route.
conflict409Resource state prevents the requested action.
validation_error422Request body, query, or path params failed schema validation. See errors array for details.
rate_limit_exceeded429Exceeded 100 requests per 10 seconds. The Retry-After header tells you when to retry.
internal_error500Unexpected server error.
error500Fallback code when no specific code applies.

CodeStatusWhen
schedule_not_found404Schedule ID doesn’t exist or doesn’t belong to your account.
schedule_archived409Attempting to update or archive an already-archived schedule.
schedule_completed409Attempting to update a completed schedule (reached runs limit or stop_at).
handler_required_for_pull400Updating a schedule to pull delivery without setting a handler.
schedule_status_changed409Concurrent update detected. The schedule’s status changed between your read and write. Retry the operation.

CodeStatusWhen
job_not_found404Job ID doesn’t exist or doesn’t belong to your account.
idempotency_key_conflict409A job with the same idempotency_key already exists on your account.

CodeStatusWhen
execution_not_found404Execution ID doesn’t exist or doesn’t belong to your account.
execution_not_running409Reporting a result for an execution that isn’t in running status. The execution may have already timed out or been reported by another process.

CodeStatusWhen
signing_key_not_found404No signing key exists for your account. Create one in the dashboard.
signing_key_grace_window_active409A key rotation is already in progress. Wait 24 hours for the grace window to close before rotating again.

When the code is validation_error, the errors array contains per-field details:

{
"success": false,
"message": "request body failed validation",
"code": "validation_error",
"errors": [
{
"field": "cron",
"message": "cron must be a valid cron expression"
},
{
"field": "payload",
"message": "payload must be 64KB or smaller"
}
]
}

Common validation messages:

MessageCause
provide either cron or interval, not bothSchedule creation/update with both timing fields
provide either delay or scheduled_for, not bothJob creation with both timing options
payload must be 64KB or smallerPayload exceeds size limit
cron must be a valid cron expressionUnparseable cron syntax
must be a valid IANA timezoneInvalid timezone string
url must use https in productionHTTP URL (HTTPS required in production)
url must not point to a private or reserved IP addressLocalhost or private network URL
stop_at must be a future datePast date for schedule termination
scheduled_for must be a future datePast date for job execution
size must be between 1 and 100Pagination page size out of range
at least one field must be providedEmpty update body

The SDK wraps API errors in ChronosApiError:

import { ChronosApiError } from '@chronos.sh/sdk';
try {
await chronos.worker.start();
} catch (err) {
if (err instanceof ChronosApiError) {
console.error(`${err.code} (${err.status}): ${err.message}`);
if (err.code === 'rate_limit_exceeded') {
// back off and retry
}
}
}

Check the code field for programmatic handling:

const res = await fetch('https://api.chronos.sh/v1/schedules', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(schedule),
});
if (!res.ok) {
const error = await res.json();
switch (error.code) {
case 'validation_error':
console.error('Invalid input:', error.errors);
break;
case 'rate_limit_exceeded':
const retryAfter = res.headers.get('Retry-After');
await sleep(Number(retryAfter) * 1000);
break;
case 'unauthorized':
throw new Error('Check your API key');
break;
}
}