DocsTroubleshooting

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:

  1. Restart the session. Close every open Claude Code session in that repo and start a fresh one. Confirm with claude mcp list in the repo folder - your dispatchseo-<site> entry should show a check mark.
  2. 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.
  3. 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.
  4. 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:

  1. No agent credential. The builder needs your project's agent credential - CLAUDE_CODE_OAUTH_TOKEN (from claude 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; .env works 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.
  2. 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.
  3. Setup isn't finished. A project whose wizard never completed is skipped on purpose. The Home page's setup cards say what's missing.
  4. 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 - set CODEX_MODEL= in .env (or the SEO_CODEX_MODEL repo variable on GitHub-Actions installs).
  5. Cursor only - the plan can't run the pinned model, or is out of usage. Free Cursor plans run only the auto model, so pinning one via CURSOR_MODEL in .env (or the SEO_CURSOR_MODEL repo 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.
  6. 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:

  1. On a company Claude plan: ask the plan's admin to enable Claude Code access. It's a seat/plan setting on their end.
  2. 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.
  3. 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 with gh 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.
  4. 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:

  1. 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.
  2. GSC_SITE_URL matches the property type exactly: sc-domain:example.com for 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 wantCommand
See what's runningdocker compose ps
App logsdocker compose logs app
Schedule logsdocker compose logs cron
Builder logsdocker compose logs builder
Follow logs livedocker compose logs -f app
Restart everythingsh start.sh
Stop everythingdocker compose down
Delete everything, including datadocker 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.