Troubleshooting
What each failure actually means, why it happens, and the fix - grouped by where things go wrong.
On this page
Every entry below follows the same shape: what you see, what it means, and what to do about it. If your problem isn't here, ask in the Discord - that's where you'll get an answer fastest. For anything worth keeping around for the next person, Discussions works too.
Windows: use Git Bash
The commands on this page are bash. On Windows run them in Git Bash (it
ships with Git), not PowerShell. Anywhere
one ends in sh start.sh, running .\start.cmd from PowerShell afterwards
works too.
Where failures surface
Every scheduled job logs its runs. A failure that persists - the same job
failing twice in a row, or missing a whole schedule window - shows as a red
banner on the dashboard Home page, and - if you set up the
failure email - lands in your inbox
too, at most one email per job per day. Urgent problems (a broken deploy,
dead credentials, an empty balance) skip the wait and surface on their
first failure. A single one-off failed run does neither: it sits in the
run log and get_cron_health, and the next scheduled run usually cures
it on its own.
A skipped job is not a failed job. Capabilities you haven't finished setting up - no Search Console yet, no keyword data source - skip quietly by design, and the Home page's setup cards tell you what's missing. This is deliberate: half-configured is a normal state during onboarding, so it never raises an alarm.
A delay is not a failure either. A machine that was asleep or powered off never emails you. Schedules catch up on their own, and only a job that fails while the stack is actually running counts as broken.
Start here
Three checks answer most problems in under a minute:
Is everything running?
In the dispatchseo folder: docker compose ps. Everything except the
one-shot migrate container should say running, and app should reach
healthy once it can talk to the database.
What does the dashboard say?
The Home page names the single next action and shows a red banner for any failed job. If it's asking for a setup step, that's your answer - nothing is broken.
What do the logs say?
docker compose logs app, or logs cron, or logs builder. The builder
in particular states plainly what it's waiting for.
Connecting your agent
You see
No such tool / server dispatchseo-... is not connected
What it means. Claude Code loads MCP connections when a session starts. A session that was already open when you added the connection cannot see it.
Fixes, in order of likelihood:
- Restart the session. Close every open Claude Code session in that repo
and start a fresh one. Confirm with
claude mcp listin the repo folder - yourdispatchseo-<site>entry should show a check mark. - You're in the VS Code extension. Its session runtime can miss a
just-added connection even after restarting the chat. Run the paste from a
plain terminal instead: open the repo folder and type
claude. Or fully reload the VS Code window. - Wrong folder. The connection is pinned to the folder you ran the connect command in. Both pastes have to happen inside your site's repo.
- The key changed. Keys rotate when a project is recreated. Copy the current connect command from Settings → Project key and run it again.
You see
401 Unauthorized from /api/mcp
What it means. The bearer token isn't valid for any project on this install.
Fix. Get the current one from Settings → Project key and re-run the
connect command. If you self-host and recently restored a database or started
over with docker compose down -v, every previously issued key is gone -
reconnect each site.
Installing and starting
You see
Port 4005 is already in use
What it means. Something else on the machine holds that port - often a second DispatchSEO stack from an earlier attempt.
Fix. Pick any free port:
echo "DISPATCH_PORT=4006" >> .env && sh start.sh
You see
Killed / exit code 137 during the build
What it means. The machine ran out of memory compiling from source. This happens on 1 GB servers when the prebuilt images can't be pulled.
Fix. Give the machine swap - spillover memory on disk - then start again:
fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile &&
swapon /swapfile && echo '/swapfile none swap sw 0 0' >> /etc/fstab
sh start.sh
The build gets slower but finishes, and the swap stays useful afterwards.
You see
A container keeps restarting, or app never turns healthy
What it means. Usually the app can't reach Postgres, or migrations haven't finished.
Fix. docker compose logs app and docker compose logs migrate have the
real error. If the database is corrupt or you're mid-experiment, starting
clean is one command - it deletes all data:
docker compose down -v && sh start.sh
You see
Pipeline update available (blue notice on Home)
What it means. The SEO workflows in your site repo are a version behind
this backend. Not a failure - sh start.sh upgrades the stack but never
touches your site repo, so the workflow files there stay as they are until
you re-apply the pipeline pack. Everything keeps publishing meanwhile.
Fix. In a terminal, inside a checkout of your site repo (not the
dispatchseo folder):
claude "Call the dispatchseo-yoursite MCP tool get_instructions with workflow install and follow it exactly."
Swap claude for codex or cursor-agent if that's your agent, and
dispatchseo-yoursite for your project's MCP server name. Merge the pull
request it opens. Full walkthrough:
Updating the pipeline in your site repo.
Nothing is being built
You see
The builder never opens a pull request
What it means. Almost always one of the following, and the logs say which:
docker compose logs builder.
Fixes:
- No agent credential. The builder needs your project's agent credential -
CLAUDE_CODE_OAUTH_TOKEN(fromclaude setup-token) for Claude Code projects,OPENAI_API_KEY(from platform.openai.com/api-keys) for Codex projects,CURSOR_API_KEY(from cursor.com/dashboard/api) for Cursor projects. Easiest is pasting it in the credential box on Settings, which verifies it before storing;.envworks too. You don't have to spot this in the logs any more: a job that comes due with no credential for its agent now writes a failed run - red banner on Home, and an email if you've set alerts up - naming what's missing, instead of being skipped in silence. - The credential got line-wrapped on paste. This one is nasty because it
looks fine. Terminals wrap long values, and copying can bring a real newline
with it, which silently invalidates them. Re-copy it in one piece and check
there's no line break in
.env. - Setup isn't finished. A project whose wizard never completed is skipped on purpose. The Home page's setup cards say what's missing.
- Codex only - the account can't run builds. A real key on an account
with no credit fails every build; the dashboard banner names it when this
happens. Add credit at platform.openai.com/settings/organization/billing.
A model 404 is the same family: builds default to
gpt-5, and some accounts can't serve it - setCODEX_MODEL=in.env(or theSEO_CODEX_MODELrepo variable on GitHub-Actions installs). - Cursor only - the plan can't run the pinned model, or is out of usage.
Free Cursor plans run only the
automodel, so pinning one viaCURSOR_MODELin.env(or theSEO_CURSOR_MODELrepo variable) fails every build with "Named models unavailable" - unset it or upgrade the plan. A drained usage pool is gentler: builds report a deferral and retry by themselves once the plan's usage resets. - Cloud only - the repo won't let Actions open pull requests. The DispatchSEO GitHub App deliberately can't change your repo's own settings, so this one is yours to flip: repo Settings → Actions → General → "Allow GitHub Actions to create and approve pull requests." The onboarding finale names it with a direct link when it applies, and re-checks by itself once you turn it on.
You see
Your organization has disabled Claude subscription access for Claude Code
What it means. Anthropic is refusing the Claude account behind your
token (the oauth_org_not_allowed flag), not the token itself. Minting a
fresh claude setup-token doesn't help: the new token carries the same flag.
Despite the "organization" wording, this hits personal Pro/Max accounts too,
usually after a lapsed or duplicate subscription.
Fixes:
- On a company Claude plan: ask the plan's admin to enable Claude Code access. It's a seat/plan setting on their end.
- On a personal plan: check claude.ai billing for a lapsed or duplicate subscription. If everything looks fine, contact Anthropic support - there's no self-serve fix for this.
- Sidestep subscriptions entirely: create an API key at
console.anthropic.com and add it to your site repo with
gh secret set ANTHROPIC_API_KEY, then delete the dead token withgh secret delete CLAUDE_CODE_OAUTH_TOKEN. The builders use the key automatically whenever no subscription token exists. This runs on metered billing against your Anthropic account. - Or switch the site to Codex or Cursor in Settings.
You see
user cancelled MCP tool call (Codex)
What it means. Codex asks for per-call approval on MCP tools by default, and on a scheduled run there is nobody to approve - so every call comes back "cancelled". It's an approval setting, not a sandbox or permission problem, so no amount of sandbox widening fixes it.
Fix. The config entry needs default_tools_approval_mode = "approve" on
each MCP server. The shipped .github/mcp-codex.toml and the builder
container both set it already - this bites when a hand-written
~/.codex/config.toml entry is missing the line. See
Use Codex instead.
You see
The queue is empty and stays empty
What it means. Research hasn't run, or it ran and rejected everything against the quality bar.
Fix. Ask your agent directly: /seo-research, or just "research keywords
for me". If it comes back with nothing, the difficulty ceiling is doing its
job - your domain isn't strong enough yet for the candidates it found. That's
information, not a bug. Adding a
keyword data source widens what it can evaluate.
You see
Approved ideas sit there for days
What it means. Publishing pace is one guide per UTC calendar day, always. A queue of ten approved ideas takes ten days to drain by design.
Fix. None needed - this is intended. Tools are different: approving a tool triggers a build immediately.
Search Console
You see
No GSC data after connecting
What it means. Either the service account isn't actually on the property, or the property string doesn't match its type.
Fix. Check both:
- The service account's email is added as a user on the property. Restricted is enough - it's what the wizard tells you to pick.
GSC_SITE_URLmatches the property type exactly:sc-domain:example.comfor a domain property,https://example.com/with the trailing slash for a URL-prefix one.
First data lands within an hour of a correct setup. See Google Search Console for the full walkthrough.
You see
Traffic numbers look lower than Search Console shows
What it means. Google's own reporting lags by a couple of days, and DispatchSEO snapshots what the API returns at the time it asks.
Fix. None - the gap closes on its own. Compare like-for-like date ranges before concluding something is wrong.
Pull requests
You see
The Merge button is a plain link instead of a button
What it means. One-tap merge needs GH_MERGE_TOKEN. Without it the
dashboard degrades gracefully to a link that opens the PR on GitHub.
Fix. Add a GitHub token with repo scope as GH_MERGE_TOKEN, or keep
merging on GitHub - nothing else depends on it.
You see
Auto-merge is on but PRs aren't merging
What it means. Auto-merge only merges PRs that are labeled seo and
green. A failing check, or a missing label, leaves it alone on purpose.
Fix. Open the PR and look at the checks. If your repo requires reviews via branch protection, auto-merge cannot satisfy that - either approve manually or adjust the rule.
Get emailed when something breaks
Optional, two minutes, and strongly recommended if you picked Automatic publish mode: in auto mode nobody opens the dashboard on a normal day, so the email is what actually tells you a job broke. In semi-automatic mode you visit weekly anyway, so the red banner already has you covered - set this up if you'd rather not wait for your next visit.
Create a free Resend account
Emails go through Resend - free tier, no credit card, no domain setup needed for this.
Create an API key
Open resend.com/api-keys, click Create API
Key, name it dispatchseo, leave the defaults, and copy the key. It
starts with re_ and is shown only once.
Add two lines to .env
On the machine running DispatchSEO (on a VPS: over SSH, not your own
computer), inside the dispatchseo folder. Replace both values. The first
part checks you're in the right folder before writing anything:
[ -f start.sh ] &&
echo "RESEND_API_KEY=re_PASTE-YOUR-KEY-HERE" >> .env &&
echo "ALERT_EMAIL=you@example.com" >> .env &&
sh start.sh ||
echo "Wrong folder - run this inside the dispatchseo folder"
ALERT_EMAIL has to match your Resend signup
Alerts go out through Resend's built-in sender, which only delivers to its own
account's address. If you've verified a domain in Resend you can send from it
to any address - set ALERT_EMAIL_FROM to something like
DispatchSEO <alerts@your-domain.com> in .env as well.
From the next run onward, failures email you. No email means everything is working.
Docker reference
Every command runs inside the dispatchseo folder the install created.
| What you want | Command |
|---|---|
| See what's running | docker compose ps |
| App logs | docker compose logs app |
| Schedule logs | docker compose logs cron |
| Builder logs | docker compose logs builder |
| Follow logs live | docker compose logs -f app |
| Restart everything | sh start.sh |
| Stop everything | docker compose down |
| Delete everything, including data | docker compose down -v |
Cron failures ride the same banner and email rails as any other install, so you don't have to watch the logs to notice a problem.
Still stuck
Ask in the Discord. Paste the failing job's log lines if it's a scheduling issue, and say whether you're on Docker, a VPS, or the cloud version - that's usually enough for someone to spot it. A thread in Discussions is the better home for a longer write-up, or for a bug that needs a fix in the repo.
Vulnerabilities go through private reporting instead, not a public thread.
DispatchSEO