Skip to content
cronpad
Troubleshooting / Troubleshooting

Troubleshooting

Start with the job’s run history (dashboard, GET /api/v1/jobs/{id}/runs, or the get_run MCP tool): most questions are answered by a run’s status, start lag and attempts. For server-level problems, check /readyz and the logs, which carry job_id and run_id on every relevant line.

A job didn’t fire

CheckHowFix
Is it active?Job statepaused → resume. disabled → read disabled_reason, fix, re-enable. completed → the schedule ran out (count, until, one-off).
When is it due?next_runs on the job, or cronpad schedule nextCheck the timezone. 0 9 * * * in UTC is 14:30 in India.
Was it skipped?Runs with status skippedMisfire policy skip, or overlap_policy: skip_if_running with a run still in progress.
Is the server scheduling?/readyz → dispatcher, queuestandby: another process holds the dispatcher lock (two servers on one data directory). queue: false: the queue file can’t be read or written; check disk space and permissions on the data volume.

Runs start late

  • Look at start_lag_ms on the run, and cronpad.queue.oldest_due_age and cronpad.dispatcher.backpressure on the server.
  • Lots of jobs due at the same second (e.g. midnight) with slow endpoints use up the worker pool. Increase workers.count, or spread schedules (5 0 * * * instead of 0 0 * * *).
  • After a restart or deploy, runs due during the downtime start late by design; see Misfires.
  • cronpad.reconciler.repaired increasing means queue entries were lost and restored. That shouldn’t happen in normal operation; check the logs for queue errors (cronpad.queue.errors) and whether something is deleting or replacing the queue file.

A webhook fails

Open the run and read each attempt.

Attempt showsLikely cause
401 / 403Wrong or expired token. Update the secret.
404Wrong URL or method.
4xx, then the run fails without retries4xx (other than 408/429) is fatal by default. Add the code to retry_on if it’s actually transient.
timeoutEndpoint slower than the job’s timeout. Raise it, or make the endpoint respond quickly and do the work asynchronously.
network: … connection refused / DNS errorsEndpoint down or unreachable from cronpad.
blocked: private addressOn cronpad Cloud (or hosted mode), the URL resolves to a private IP. Use a public endpoint.
secret NAME not foundThe job references a deleted secret.

Send test on the job reproduces one request with full details, without recording a run.

My endpoint received the same run twice

That’s at-least-once delivery doing its job after a crash, a timeout, or a lease loss. Deduplicate on Idempotency-Key / webhook-id (the run ID, identical across attempts). See Delivery guarantees. Cronpad-Attempt tells you whether it was a retry or a redelivery of the same attempt.

Signature verification fails

  • Verify against the raw body bytes, before any JSON parsing or re-serialization.
  • Use the full secret including decoding the part after whsec_ from base64.
  • Signed content is {webhook-id}.{webhook-timestamp}.{body}.
  • Check your server clock: timestamps older than 5 minutes are rejected by the reference code.
  • After a rotation, both secrets are valid for 24 hours; make sure you deployed the new one.

A job was disabled

It had too many consecutive failed runs (20 by default). disabled_reason says how many; the last runs say why. Fix the cause, then Re-enable. See Auto-disable.

The server won’t start

Log messageFix
config: … (a list of problems)Each line names a setting and what’s wrong with it.
no sign-in method configuredConfigure Google or GitHub, or auth.dev_login = true for local testing only.
database is locked at startupAnother cronpad process is using the same files. Run exactly one.
the [redis] section is ignored (warning)Left over from a Redis-based release. Remove the [redis] section or CRONPAD_REDIS__* variables; the queue is now a SQLite file.
queue: …The queue file couldn’t be opened or created. Check that the data directory is writable. A corrupt file is moved aside as *-queue.db.corrupt-<time> and recreated automatically.
secrets: cannot decryptsecrets.encryption_key differs from the key the data was encrypted with.

Still stuck

Open an issue with the cronpad version (cronpad version), the X-Request-ID or run_id, and the relevant log lines (they’re already redacted).