Troubleshooting
Locate the failing layer before restarting everything.
herdr-mcp spans Herdr, the local runtime, workstation link, Cloudflare Edge, OAuth/MCP and browser continuity. The fastest diagnosis is to find the broken layer first.
Use this order:
Herdr
↓
local herdr-mcp runtime
↓
workstation link
↓
Cloudflare Edge
↓
OAuth / MCP
↓
ChatGPT tool snapshot
↓
browser continuity
If one layer is broken, do not start by reconfiguring a later one.
30-second triage#
Is the local HTTP runtime listening?#
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8772/
200 or 401 means a process is listening. Connection failure means the runtime is down, on another port or otherwise unreachable.
On macOS LaunchAgent installations:
herdr-mcp status
herdr-mcp logs
herdr-mcp permissions status
If file/git tools return macos_tcc_access_blocked, run herdr-mcp permissions setup, grant access to herdr-mcp-broker, then herdr-mcp permissions verify. Ordinary not_found errors are unrelated.
Is Herdr itself available?#
herdr --version
herdr api schema >/dev/null
If local HTTP works but herdr_inspect cannot see real workspaces, investigate the Herdr daemon/socket before touching Cloudflare.
Is the binary installed but off the shell PATH?#
ls -l ~/.local/bin/herdr-mcp
zsh -ic 'command -v herdr-mcp'
zsh -lc 'command -v herdr-mcp'
A present binary with an empty command -v result is installed_but_not_on_shell_path, not a missing installation. Run export PATH="$HOME/.local/bin:$PATH" for the current process. For zsh, persist the same line idempotently with line='export PATH="$HOME/.local/bin:$PATH"' and grep -Fqx "$line" "$HOME/.zprofile" 2>/dev/null || printf '\n%s\n' "$line" >> "$HOME/.zprofile". Do not reinstall or create a second PATH owner.
Can Edge see the workstation?#
OAuth may succeed while the workstation is offline. Public login success does not prove the local development machine is connected.
Check Edge health/status and the workstation link.
Is the link blocked by the local network?#
The Link reuses proxies that already exist in the environment. Resolution order:
HERDR_LINK_PROXY > HTTPS_PROXY/https_proxy > HTTP_PROXY/http_proxy > ALL_PROXY/all_proxy
> macOS system proxy (scutil --proxy: HTTPS, then HTTP, then SOCKS)
Details that matter when workers.dev is unreachable:
socks5://andsocks5h://are supported. SOCKS5 dials use remote-DNS semantics: the hostname is sent to the proxy unresolved, so a locally polluted DNS resolver cannot breakworkers.devconnectivity.- Proxy authentication (HTTP Basic or SOCKS5 username/password) is not supported; proxy URLs with embedded credentials are rejected, and credentials never appear in status or error output.
- On macOS, a PAC configuration is detected but never evaluated. Link does not fetch or execute PAC scripts; with only a PAC configured, the Link connects directly.
- Do not "fix" link connectivity by disabling TLS, rewriting system proxy settings, or turning the Link into a general-purpose forwarder.
Does a new ChatGPT conversation get the current catalog?#
The current first-party DEV/PROD public ChatGPT contract is epoch 8 / 19 actions. Workstation execution is epoch 5 / 18 tools, including herdr_skill; the extra public action is Edge-local herdr_devices. The immediately previous frozen epoch-4 contract and the frozen epoch-2 catalog are retained only as bounded rollback/compatibility baselines; epoch 3 is historical only, and the public Edge epoch-3 identity remains a bounded non-DEV/PROD baseline.
Old conversations may retain an older tools/list snapshot. Before reinstalling anything, verify the server and open a new conversation.
Symptom: Cloudflare API returns 403 while the token verifies as valid#
/user/tokens/verify only proves the token is active. A 403 on a specific call names a missing permission:
GET .../accounts/<id>/workers/subdomainfails → Account Settings Read is missing;- Worker script deploy calls fail → Workers Scripts Edit is missing;
- R2 provisioning fails → the optional Workers R2 Storage Edit was never granted. The core install does not need it; this is an error only when the user explicitly enabled the artifact relay.
Grant exactly the missing permission and retry. Do not recreate a broader token blindly and do not report it as a generic deployment failure.
Symptom: one hostname fails while another hostname of the same Worker works#
If *.workers.dev times out or fails DNS/TLS while the same Worker's Custom Domain returns /health 200 (or the reverse), the Worker code is healthy and the failure is hostname/DNS/network-path specific. Do not redeploy the Worker and do not create another Worker/R2/Connector. Prefer a Custom Domain as the stable production origin when the user owns a domain; otherwise keep workers.dev and let the Link transport fallback (direct → validated local proxy → shared relay) handle the network path.
Symptom: Connector cannot be added or OAuth loops#
Check:
- MCP URL is
https://<stable-origin>/mcp; - public base URL / OAuth issuer use the same origin without
/mcp; - protected-resource and authorization-server metadata are reachable;
- the Worker deployment is the intended one;
- the ChatGPT Workspace permits Developer/custom MCP apps.
If an OAuth token is successfully issued and the next step fails, you are probably past authentication and into MCP discovery/routing.
See ChatGPT Connector.
Symptom: Connector says connected, but the chat has 0 tools#
Connector installation and conversation tool acceptance are different steps.
Check in this order:
- open a new conversation;
- confirm current public contract/version;
- verify
tools/listsucceeds; - check whether one incompatible
inputSchemais causing the catalog to be rejected; - distinguish a stale conversation snapshot from a server problem.
If new conversations see 18 tools and an old one sees 17, the server is usually fine.
Symptom: tools are visible but herdr_inspect reports workstation offline#
This is no longer a ChatGPT schema problem. If ChatGPT received a structured workstation_offline result, the ChatGPT → MCP Edge path was alive enough to return that result; Edge did not have a usable Link WebSocket for the selected workstation. The browser extension does not make this decision.
Current recovery is layered rather than "restart everything":
- A recently connected workstation gets up to 15 seconds of process-local reconnect grace at Edge. A validated Link
hellowakes the pending request immediately. This grace does not write Durable Object storage or alarms. - If the workstation is still unavailable, Edge returns machine-readable recovery metadata:
retryable=true,delivery_state=not_delivered,retry_after_ms=5000, and a read-onlyherdr_inspectprobe policy with 5s / 10s / 20s backoff. - The local Link keeps its normal reconnect/backoff loop. A successful Online transition clears the prolonged-offline timer.
- If the Link cannot become Online continuously for 300 seconds, it exits with diagnostic evidence so launchd
KeepAlivecan start a freshdev.herdr-mcp.link-prodprocess. - The server health watchdog remains responsible only for an actually unhealthy local server.
workstation_offlineby itself is not a reason to restart a healthydev.herdr-mcp.server.
The replay rule is deliberately stricter than the retry hint:
delivery_state=not_delivered: the request did not reach the workstation; after recovery it may be sent again. Reuse an idempotency key when the operation has one.delivery_state=delivery_unknown: a read may be retried when explicitly marked retryable, but a mutation must be reconciled against state/evidence before any replay.delivery_state=deliveredor no proof of non-delivery: do not replay a mutation merely because the connection later failed.
Check the workstation in this order:
herdr-mcp status
herdr-mcp link status
launchctl print gui/$(id -u)/dev.herdr-mcp.link-prod
tail -n 100 ~/.config/herdr-mcp/link-prod.launchd.err.log
Then confirm workstation identity, active runtime generation health, and Edge's recent Link state. Do not delete/recreate the Connector to fix a workstation-link problem, and do not turn a Link-only failure into a global herdr-mcp service restart unless local server health is also bad.
Symptom: inspect works, but file operations fail#
Common gates:
- path is outside a managed Git root and outside a non-Git operational root proven by an exact live workspace/pane cwd (a directory borrowed from another project, or a sibling the live topology does not prove, is refused);
- filename matches a secret-ish path rule;
- read-only mode is enabled;
- target root is not in the write allowlist;
- the file is already dirty and explicit acknowledgement is required;
- another worker is active in the project and the busy gate rejects concurrent writes.
Read the structured error first. Do not make shell bypass the default answer to every file gate.
herdr_exec is intentionally a stronger boundary than herdr_fs_* and does not provide the same secret-path filtering.
Symptom: transient TaskGroup / ExceptionGroup control-plane errors#
You may see a failed snapshot/pane operation even though the agent or repository is fine.
herdr-mcp can degrade some read paths to narrower evidence sources such as:
- list APIs instead of one large snapshot;
- deterministic Git state;
- direct managed-root file operations.
Re-run herdr_inspect / herdr_since to obtain current facts. Do not treat one control-plane blip as proof the Git project is unusable.
Symptom: prompt or exec timed out and you do not know whether it ran#
The rule is do not blindly retry a mutation.
herdr_prompt#
Normal long-running work should not keep the parent blocked. herdr_prompt submits once and immediately returns task_id / dispatch_id / Runtime turn_id, then the parent continues other work while Runtime tracks that exact task through herdr_mcp.agent.task.status / herdr_mcp.agent.task.inbox. If a caller explicitly uses compatibility wait, it waits on this task_id, not on a stale global idle/done state. A wait timeout still does not mean non-delivery; inspect the returned task/inbox before retrying. Reuse an idempotency_key for a repeated intent.
herdr_exec#
If the command was already delivered to a visible pane, a later control-plane timeout is not permission to send the command again. Inspect the pane, Git state, files and tests.
“No success response reached the client” does not mean “nothing happened.”
Symptom: local agent finished but ChatGPT did not continue#
This is often not an MCP failure.
MCP provides:
ChatGPT → workstation
For an Agent task dispatched by the current WebChat, Runtime first records the terminal result in the durable parent inbox. The browser extension then reads the inbox scoped to the current browser session_ref and can create the next WebChat turn; the parent does not need to keep its original turn blocked while the child works.
If the agent finished but ChatGPT did not continue, verify that:
- Native Messaging host is healthy;
- the conversation has a stable
browser_session_refand is bound to the correct workspace; - the relevant Auto scope is enabled;
- the task is terminal and unacknowledged in
herdr_mcp.agent.task.inbox; - read the inbox with
advisory=truewhen you need Parent guidance. Runtime evaluates at most one frozen attention batch when unacknowledged terminal work exists;verify_completionmeans enter deterministic validation andcontinue_unobservedmeans leave independent running siblings alone. Jev timeout/error/unconfigured state never suppresses deterministic terminal wake, and the Browser has no second semantic wait loop.
A manual HUD continue action remains an explicit fallback. See Browser continuity.
Symptom: HUD shows the wrong workspace name#
Binding identity is the workspace_id; the label is display data.
If the ID is correct but the label is stale, the extension should refresh it from the live workspace catalog. Do not remove a correct binding merely to fix display text.
If the ID itself is wrong, bind to the correct workspace.
Symptom: standalone status looks current, but Chrome still runs an older/unexpected unpacked build#
This is a path-ownership drift. herdr-mcp extension standalone status proves what Herdr manages on disk; it does not prove which unpacked directory an already-configured Chrome profile is loading. On macOS run:
herdr-mcp doctor
If the default doctor summary reports standalone extension path drift, run herdr-mcp doctor --verbose to see the expected and actual paths, or herdr-mcp doctor --json and inspect standalone_extension for the same machine-readable evidence, including the Chrome profile and drift_count. Open chrome://extensions, locate Herdr, and load/reload it from expected_path (normally ~/.config/herdr-mcp/extensions/standalone/current). Then rerun doctor. Do not change Native Host ownership or delete extension data unless a separate identity problem is also proven. The doctor probe is read-only and inspects only the exact Herdr extension ID entry in Chrome preferences.
Symptom: Browser Control Center will not open, shows no workspaces, or stays on Runtime unavailable#
Separate a Side Panel UI problem, a Native Messaging identity problem, and a runtime problem:
- Click the Herdr toolbar icon and verify Chrome opens the Control Center Side Panel directly; do not navigate to
control-center.htmlas a normal web page. herdr-mcp status/herdr-mcp doctorshould first prove the local runtime is healthy.herdr-mcp native-host statusshould report the Native Messaging host registered.- If Chrome just updated the Store extension, refresh the affected web page (or restart Chrome if needed) so the current content script is loaded.
herdr-mcp native-host statusshould report the extension identity/channel you intentionally selected and a Native Host runtime consistent with the active runtime generation. Current runtimes support STORE / STANDALONE / DEV; older runtimes may expose fewer channels. Do not repair an origin mismatch by guessing another channel: inspect the installed runtime's supported commands and the actual Chrome extension identity first.Runtime healthy · event stream reconnectingmeans the panel still has a snapshot while incremental events recover; it does not mean the whole local runtime is down. Use Refresh for an authoritative reconciliation.
Send instruction executes through the trusted local control route; Adjust current task returns an exact provider capability/outcome and never silently becomes Prompt. Terminal-only panes can run a command through the fenced pane.send_input + Enter path. Arbitrary Herdr API remains Preview-only. If any mutation reports uncertain, inspect live state before retrying. If Steer reports session_not_resolved, the selected provider session lacks a verifiable control endpoint/thread/active-turn mapping; that is a capability result, not a transport failure.
Symptom: ChatGPT response is partial, disconnected or shows send timeout#
Do not immediately resend the original task. Tool mutations may already have occurred.
Continuity recovery is evidence-first:
- inspect same-origin conversation state when available;
- if server state is ahead of the DOM, refresh/synchronize the view;
- retry only when evidence says the request was not accepted;
- fail closed on uncertain delivery;
- consider handoff only after normal recovery is exhausted.
If automatic recovery cannot obtain trustworthy evidence, refresh manually and use Herdr monitor to re-read local state before continuing.
See Wake, recovery and handoff.
Symptom: ChatGPT Queue did not send immediately or queued content is still pending#
Queue is intentionally not an immediate send. While the assistant turn is live, content should remain in the current conversation's durable queue and send only after the turn settles, before generic auto-continue.
Check:
- the page is ChatGPT; other sites do not currently expose the same Queue UI;
- whether the assistant is still generating, using tools, or waiting on a permission card; a live turn must not be interrupted by Queue;
- a
turn-in-progressor uncertain submit is not ACKed or dropped; - clicking Queue with an empty composer can retry a still-pending batch;
- right-clicking Queue explicitly clears the current conversation queue;
- after confirmed handoff, pending entries move to the new conversation and should not replay from the source.
Content disappearing without confirmed delivery is the actual reliability failure. Capture the conversation, current turn state, and browser console evidence before filing an issue.
Symptom: Manual handoff is unavailable#
Use Handoff in the in-page HUD. If it is unavailable or disabled, verify:
- the current site/conversation type supports handoff;
- if a workspace is bound, it has no active working agent;
- no transfer is already active.
The current scope may be Auto on or Auto off. Where handoff is supported, the target conversation inherits the source Auto state and source automatic wakes pause during transfer.
Handoff must create the packet, create the new conversation, and verify the seed before cutover. If a workspace binding exists, only then switch or migrate that binding. If the transfer is recoverable/uncertain, keep any existing binding as the safety anchor instead of manually unbinding it.
Symptom: z.ai / DeepSeek stops after printing a JSON tool call#
That is the JSON→MCP bridge, not the ChatGPT Connector.
Check:
- Native Messaging host;
- local MCP tool catalog;
- stable conversation identity;
- whether the last real assistant message is still a tool-call JSON object;
- whether a
TOOL_RESULTwas returned; - whether enough bridge context survived a page reload to resume safely.
Do not treat internal tool JSON as the final natural-language answer.
See JSON → MCP bridge.
Symptom: Chromium asks for local-device / loopback permission#
Some Chrome/Chromium profiles apply a separate permission gate to local loopback access.
Check the extension site/local-device permission in the browser's extension settings. Native Messaging is the primary trusted path, but diagnostics/compatibility paths can still surface loopback permissions.
Do not rotate Herdr credentials just because a browser permission is pending.
Symptom: Cloudflare deployment fails#
Separate these cases:
- credential/identity failure;
- build/test failure;
- Worker deployed but health/routing failed;
- workers.dev works but Custom Domain/DNS fails.
Use least-privilege credentials and fix the specific layer. Do not turn a route problem into an account-admin token.
See Cloudflare Edge credentials and Cloudflare Edge deployment.
Symptom: runtime upgrade broke local behavior#
For an implementation change inside the same contract epoch:
bin/herdr-runtime-generation status
Inspect active/previous generations and use the Runtime A/B rollback path when appropriate.
If the tool catalog/schema changed, this is a contract migration, not an ordinary runtime A/B problem. Do not use herdr-self-update to sneak across an epoch boundary.
See Runtime A/B.
Useful evidence for an issue#
Useful, non-secret diagnostics include:
boot_id;- runtime version / contract epoch;
- workstation id;
- failing tool;
- failure phase / delivery state;
- whether Git/pane/agent state changed around the failure;
- Edge health/workstation status;
- whether the ChatGPT conversation was new or old;
- bound workspace identity.
Remove bearer tokens, OAuth JWTs, Cloudflare secrets and sensitive project content before sharing logs.
Restart last, not first#
Restarting can restore service, but it can also erase the evidence that explains the root cause.
Prefer:
- capture current state;
- identify the failing layer;
- restart only the relevant component;
- verify that layer and the next layer afterward.
That turns “it started working again” into an actual diagnosis.