Skip to content
Browse the docs

DocsKeep it running

Troubleshooting

The errors people actually hit — a taken bucket name, the cron-trigger limit, a 503, silent scheduling — and what fixes each.

On this page

Start with npm run doctor -- --app-url <url>: it is read-only, and every failure it finds prints the command that fixes it. The cases below are the ones it cannot fix for you.

The R2 step says the bucket name already exists

R2 bucket names are unique across all Cloudflare accounts, so cogsend-media is only a starting point. Set bucket_name in wrangler.personal.jsonc (or pass --bucket my-cogsend-media to setup) and deploy again. R2 also refuses to create anything until the account has a payment method on file, even for free-tier usage.

The deploy complains about cron triggers (10072)

✘ [ERROR] Trigger configuration for "…" was only partially updated:
    - This account has reached the Workers Free limit of 5 cron triggers per account … [code: 10072]
Failed: error occurred while running deploy command

The Worker and its assets were uploaded — only the schedule was refused. This is an account limit, not a per-Worker one: five cron triggers in total on the free plan, across every Worker you run, and a fresh project cannot get a sixth slot.

npm run deploy handles this for you: it retries once with the trigger removed ("crons": []) and exits 0, printing the same explanation, so the build is not marked failed and the app is live. Publishing now works; only scheduled posts need a tick. Set COGSEND_STRICT_CRON=1 to get the plain failure instead.

Pick one of these:

  1. Free a slot. Cloudflare → Workers & Pages → the other Worker → Settings → Trigger events → Cron triggers → delete a schedule you no longer need.

  2. Upgrade the account to Workers Paid (hundreds of triggers).

  3. Use an external cron and leave the Worker without a trigger. Settings → Scheduled publishing shows the tick URL and can generate a token; paste both into any cron service:

    curl -X POST https://your-worker.workers.dev/api/internal/tick \
      -H "Authorization: Bearer <tick token>" \
      -H "Content-Type: application/json"

    cron-job.org runs a job once a minute on its free plan, UptimeRobot’s free plan every five minutes, and the repository ships a GitHub Actions workflow (.github/workflows/scheduler-tick.yml) that needs repository secrets APP_URL and SCHEDULER_SECRET. Ticks are idempotent, so a duplicated or delayed caller is harmless. To go trigger-less by choice, delete the triggers block from your config (or set "crons": []).

    Using the env secret instead of the generated token works too: set SCHEDULER_SECRET and keep the Worker secret and the caller in sync.

npm run doctor -- --app-url https://your-worker.workers.dev reports which of these you are in: ticks arriving, no trigger configured, or a refused trigger.

The app answers 503: “must not be an example value”

The deployment is running on the example secrets from .dev.vars.example. Set a real one — openssl rand -hex 32 generates a key — and redeploy:

node scripts/wrangler.mjs secret put APP_ENCRYPTION_KEY
npm run deploy

npm run doctor -- --app-url <url> reports this case directly.

Scheduled posts never fire

Open Settings → Scheduled publishing in the app: it says whether a tick has ever arrived, why one is missing when the last deploy could not attach the trigger, and offers both a token and a “Tick now” button.

Behind it: the tick runs every minute from the cron trigger in wrangler.jsonc (Cloudflare → Settings → Trigger events), or from an external cron calling POST /api/internal/tick with a bearer credential. The built-in cron derives its own credential from APP_ENCRYPTION_KEY; external callers use the token from that Settings card, or the env SCHEDULER_SECRET / API_TOKEN. A read-only check reports the scheduler line:

npm run doctor -- --app-url <url>

Missing migrations

After pulling new code:

npm run db:migrate:remote
npm run deploy

or npm run deploy:release, which runs tests, migrations, build and deploy in one go.

Two instances in one Cloudflare account

Give the second instance its own names, or it will adopt the first one’s resources: D1 provisioning matches on database_name, and R2 bucket names are global. npm run setup -- --name my-cogsend --db my-cogsend --bucket my-cogsend-media writes them into wrangler.personal.jsonc (gitignored) — the Worker name, the database’s name and its id, and the bucket — so the deploy output, wrangler d1 … and npm run doctor all name the instance you think they do.

Still stuck?

Open an issue with the output of npm run doctor and the version shown in Settings → Instance.

Where to go next

Written for CogSend 1.3.2. This page lives in the app repo, next to the code it describes. Something wrong or missing? Edit docs/troubleshooting.md or open an issue.