Troubleshooting
Find your symptom. Each entry says what the cause actually is and what to do about it.
If a paid tool timed out, failed, or gave an ambiguous answer, do not call it again. run_canvas_workflow, generate_image_in_canvas, and generate_video each start real provider work. Jump to Paid work looks stuck before doing anything else.
Start here: three checks that isolate the problem
Run them in order. Each one rules out a layer.
Is the transport up?
Ask the agent to call guide_search with any query.
That tool requires no scope at all. If it succeeds, transport and authentication are fine and your problem is permissions or usage. If it fails, nothing else will work — the problem is connection or auth.
Is the token delegating the right account?
Call canvas_list.
If it returns canvases, your token works and is attached to the account you expect. An empty list on an account that definitely has canvases means you approved a different Gavana account during OAuth than the one you were thinking of.
How many tools are registered?
Check your client’s MCP panel.
| Count | Means |
|---|---|
| 29 | Hosted endpoint |
| 57 | Local stdio server |
| Fewer than 57 on a local server | GAVANA_MCP_TOOLSETS, GAVANA_MCP_TOOLS, GAVANA_MCP_EXCLUDE_TOOLS, or GAVANA_MCP_READ_ONLY is set |
| 0, or the server shows as failed | Connection problem — see below |
Connection problems
The server will not connect at all
| Check | Detail |
|---|---|
| The URL | https://app.gavana.ai/mcp. No trailing slash, no path after it. |
| The transport | Streamable HTTP. If your client asks for a type, it is HTTP or “remote”, never stdio. |
| Whether a browser opened | The hosted endpoints authenticate through browser OAuth. If no browser opened, the client did not start the flow — check for a blocked pop-up. |
| Workspace policy | Some ChatGPT and Claude workspaces require an administrator to allow custom connectors first. |
405 method_not_allowed on a GET
Expected, not a fault. The hosted endpoints are stateless JSON-RPC over POST. A GET returns 405 with an Allow: POST, OPTIONS header. If your client only ever gets 405, it is probing rather than speaking MCP — confirm it is registered as a streamable-HTTP MCP server rather than a plain web hook.
403 access_denied mentioning Origin
Your browser-based client is sending an Origin header Gavana does not accept. The hosted endpoints allow https://chatgpt.com, https://claude.ai, and the Gavana origin itself, plus anything the deployment explicitly permits.
Non-browser clients send no Origin header and are unaffected — so if you are seeing this from a terminal client, something in between is adding the header.
The local stdio server will not start
| Symptom | Cause | Fix |
|---|---|---|
npx cannot resolve @gavana.ai/mcp | The package is published publicly at 0.2.0, so this means npm is being proxied through a private registry that does not mirror it | Point npm at the public registry for this scope, or use a hosted endpoint. With application repository access, bun run canvas:mcp runs it from source. |
| Node version error | Node.js 20 or newer is required | Upgrade Node. |
| Starts, then every call fails auth | No credential reached the process | Set GAVANA_BASE_URL and GAVANA_AGENT_TOKEN, or run gavana auth login to write ~/.config/gavana/agent.json. |
| Wrong package name | It is @gavana.ai/mcp, with the .ai | A name without .ai does not resolve. |
Missing or unexpected tools
A tool the docs mention is not in my client’s list
| You see | Cause |
|---|---|
29 tools, and node_create is absent | node_*, connection_*, action_*, recipe_search, recipe_fork, asset_list, canvas_render, and provider_list exist only on the local stdio server. |
| Fewer tools than expected locally | GAVANA_MCP_TOOLSETS, GAVANA_MCP_TOOLS, GAVANA_MCP_EXCLUDE_TOOLS, or GAVANA_MCP_READ_ONLY is set in the server’s environment. |
find_video_models returns 403 insufficient_scope
The hosted tools reference lists it as read-only, which is accurate — looking up a model mutates nothing. It still requires the video:generate scope, because discovering video models is part of the generation path. A token without that scope is refused before the handler runs.
The tool list changed after I edited the URL
Some clients keep the original OAuth grant when you edit a server’s URL in place. Remove the server and add it again rather than editing:
claude mcp remove gavana && claude mcp add --transport http gavana https://app.gavana.ai/mcpAuthentication and scope errors
401 invalid_token
The token is missing, malformed, expired, or revoked.
- Hosted: reconnect the client and complete OAuth again.
- Local: Agent Access tokens default to Never expiry. If this token was created with a custom 1–90 day expiry, create a replacement and update the client’s environment.
- Check you are not passing a token in a URL or a custom header. Gavana accepts bearer tokens in the
Authorizationheader only.
403 insufficient_scope
The token is valid but lacks a scope the tool needs. The error message names the missing scopes, and the WWW-Authenticate header repeats them.
The scope check happens before the handler runs, so nothing partial occurred and no paid work started.
Common cases:
| Tool | Missing scope, usually |
|---|---|
save_image_to_canvas | image:generate — that scope governs writing image bytes into Gavana, not just generating them. canvas:write alone is not enough. |
run_canvas_workflow, generate_image_in_canvas, generate_video | job:manage — without it you can start work but cannot poll, wait for, or cancel it. |
find_video_models | video:generate |
Token creation rejects my scope selection
Scope dependencies are enforced at creation, not silently added:
canvas:writerequirescanvas:readelement:writerequireselement:readimage:generaterequirescanvas:read,canvas:write, andasset:readvideo:generaterequirescanvas:readandasset:read
The error names exactly which are missing. The token creation form selects dependants for you.
It works, but touches the wrong account
You approved a different Gavana account during OAuth. Disconnect and reconnect, watching which account you sign in as. For a local token, the token itself is bound to the account that created it — create one from the right account.
Canvas errors
409 on canvas_apply_batch or a canvas_validate dry-run
The canvas changed after you read the revision you passed. Someone else’s work is now in the graph.
Re-read
canvas_get for the current revision and graph.
Look at what changed
If they added a Section where yours was going, your coordinates now collide. If they deleted the node you meant to delete, that operation will fail.
Rebase, preserving their change
Move your nodes. Never move theirs.
Decide on the key
Byte-identical payload after rebasing? Reuse the key. Anything changed? New key.
Do not resolve a 409 by omitting baseRevision. That removes conflict detection, not the conflict — the write then silently lands on top of whatever is there.
Two 409s in a row means someone is actively working in that canvas. Say so and stop rather than racing them.
404 on a canvas or node
The handle is wrong. Get canvas handles from canvas_list and node handles from canvas_get. Do not reconstruct a handle from parts, and do not truncate one to fit.
The batch was rejected and nothing happened
Correct behaviour. canvas_apply_batch is atomic — one invalid operation means no partial graph. The error names the failing operation.
Fix that operation. Do not resend the batch minus the failure unless dropping it is genuinely your intent.
malformed_section in the validation output
A Section is a text node with metadata.isSection: true, at least 200 × 160. Any other node type, or a smaller one, is an error.
Rejected for writing metadata.content
Media is server-owned. You cannot write image or video bytes, storage keys, or arbitrary media URLs into node metadata. A non-empty metadata.content is rejected when the node is, or is becoming, an image or video.
Use save_image_to_canvas or a generation tool.
canvas_validate says summary.truncated
Findings hit the 50-per-code cap, or overlap analysis gave up. Overlap detection buckets nodes into a 2,048-unit grid, refuses any node spanning more than 64 cells, and stops after 100,000 comparisons.
Fix the findings you can see and run it again — the next pass surfaces what was cut. Meanwhile, say your report is incomplete.
summary.passed is true but something is still wrong
passed only means zero errors. reviewRequired is the field to read: it is true when there are errors, or warnings, or truncation.
Paid work looks stuck
This is the section where the wrong move costs money.
A paid tool returned while still queued
This is normal and it is not a failure. The tool waited waitSeconds — 45 by default, 50 maximum — and handed you a resumable handle because the work was not finished.
Do not call the paid tool again. The original run is still going. A second call starts a second paid run.
Poll instead:
| Started with | Poll with | Handle shape |
|---|---|---|
run_canvas_workflow | get_canvas_workflow_run | run: |
generate_video | get_video_job | job: followed by a UUID |
Polling is free. Call it again while the status is queued or running.
Raising waitSeconds does not make generation faster. It only changes how long the tool blocks before giving you the handle.
A paid run failed terminally
Report it and stop. Ask for fresh intent before any new attempt.
Do not automatically retry a terminal failure, a timeout, a disconnect, or an ambiguous provider response with a new key. The idempotency key protects against a duplicate request; it does not make a second deliberate attempt free.
400 “not a video generation” or “not a reusable workflow run”
You passed the wrong kind of handle. get_video_job takes a job: handle from generate_video; get_canvas_workflow_run takes a run: handle from run_canvas_workflow. They are not interchangeable.
A run succeeded but I cannot find the image
A run reporting success is not the same as durable canvas content. Read the canvas:
{ "name": "canvas_get", "arguments": { "canvasId": "canvas:abc123:product-shots" } }Check the output node exists, is of type image, and carries server-owned media rather than being an empty placeholder. A video job in particular may return a protected download link without materialising a native video node — report what the server actually returned rather than assuming durability.
The agent started generating without being asked
The server’s instructions tell the model to require explicit current-turn intent, but instructions are not enforcement. The hosted endpoint registers the paid tools and the token holds the scopes that can run them.
If this happened, treat it as a contract break: tell the agent to stop, and require explicit current-turn approval before any further paid call. For unattended local work, set GAVANA_MCP_READ_ONLY=true or issue a token without generation scopes.
Image errors
| Symptom | Cause | Fix |
|---|---|---|
400 “not an image” from get_canvas_image | The node is text, sticky, or video | Read the canvas and pick an image node. |
409 “does not have a stored result” | Empty placeholder — generation unfinished or never started | Check the run or job. Do not start a new generation to work around it. |
413 | Source over 50 MB, or the re-encoded preview still over 5 MB | Use the full-resolution link instead of the inline preview. |
415 | Stored bytes are not PNG, JPEG, WebP, or GIF | Report it; there is no client-side fix. |
save_image_to_canvas rejects the URL | Not HTTPS, or over 4,096 characters | Use an HTTPS URL. Plain http:// is refused. |
Getting help
When you report a problem, include:
- The
X-Request-IDfrom the failing response. It identifies the exact request in Gavana’s logs. - The tool name and the HTTP status or error code.
- Which server you are on — hosted
/mcp, or local stdio. - The canvas handle, if the problem is canvas-specific.
Send the X-Request-ID and nothing else that is sensitive. Never include an Agent Access token, an OAuth token, a screenshot showing one, or canvas content you would not publish. The request ID is enough to find the failure without any of that.