Skip to main content

Troubleshooting: Agent Studio graph and custom-tool validation

The Agent Studio canvas (see Build Agents Visually in the Agent Studio Canvas) can reject a publish or a test-run, and the custom-tool registry rejects registration or invocation, each with its own validation code. The runtime-error family (agent runtime errors) covers the codes that fire mid-turn; this page owns the validation family that fires at publish, register, or wiring time, and — in Custom tool runtime failures — the dispatch family that fires later, when a registered executor misbehaves mid-call. Every failure here arrives as a structured envelope — key on error.code, and for GRAPH_VALIDATION_FAILED read the per-issue list in error.details before you touch the canvas:

Graph publish validation

Custom tool registration

These codes come from POST /agents/custom-tools and the invoke path (see Build custom tools for AI agents). Sample INVALID_EXECUTOR_URL envelope:

Tool wiring errors

These fire when a graph, registry, or agent reference points at a KB, a handoff target, or a runtime handler that cannot resolve.

Custom tool runtime failures

Registration proves your executor URL is reachable and public; dispatch is where production reality shows up. When the agent invokes a registered custom tool (or when you fire the tool’s test endpoint), the platform dispatches a signed POST to your executor_url and classifies whatever comes back into one of five codes. Unlike the 422s above, these are not publish-time gates: they fire per call, against a live executor.

The dispatch contract

  • Per-tool dispatch timeout. Every tool carries a timeout_ms (1,000 to 60,000, default 30,000). One dispatch that overruns it ends the attempt; if the tool configures a retry policy, each retry attempt gets its own timeout, and the whole sequence is capped by the agent execution budget.
  • Response cap. A response body is capped at 1 MiB; a larger body is cut off mid-stream and the attempt fails.
  • Who owns what. A 4xx or 5xx status, a slow response, or a broken TLS chain is your executor’s fault. A dispatch fault that happens before your executor is even reached is the platform’s side. The table below assigns each code to a side.
This mirrors the MCP surface (see Bring your own MCP servers): there, discovery carries an 8-second tools/list handshake timeout and each tools/call is bounded by the same per-tool timeout_ms (default 30 s). The HTTP custom-tool surface skips the discovery step, so the per-tool dispatch window is the only clock.

Decoding the codes

Every dispatch failure result carries error_code — one of the five codes — and a sanitized error line that never leaks the raw executor or network exception. Where the tool configures a retry policy, the recorded attempt history keeps one entry per attempt with its own status, duration_ms, and raw error, and the surfaced code is the terminal outcome after retries.

What to capture

  • meta.request_id from the envelope.
  • The tool name and the tenant.
  • duration_ms per attempt: near timeout_ms says slow executor; near-zero says DNS or connection refused; a platform-side error string on a fast attempt says dispatch fault.
  • The executor’s own status code from the attempt row, and your executor’s logs at the same timestamp.

What NOT to do

  • Do not blind-retry CUSTOM_TOOL_CLIENT_ERROR. A 4xx is your executor deterministically rejecting the request; a loop only burns your retry budget.
  • Do not loop on CUSTOM_TOOL_SERVER_ERROR either. A crashing executor keeps crashing; fix the executor first, then retry.
  • Do not raise timeout_ms to hide a slow executor. The ceiling is 60,000 and the agent execution budget still caps the whole turn; a tool that needs minutes wants the async job-id pattern, not a bigger window.
  • Do not treat CUSTOM_TOOL_NETWORK_ERROR as a platform outage. TLS and DNS faults on a public HTTPS URL are executor-side; the platform pins the resolved address for the dispatch exactly as validated at registration.

When to escalate for runtime codes

Escalate only CUSTOM_TOOL_DISPATCH_FAILED, the platform-side class, with the request_id and the attempt rows. Timeout, network, server, and client classes are executor-side and carry everything you need to fix them yourself.

KB re-chunking

Decision checklist

Run this after any of the codes above:
  1. Read error.code on the envelope.
  2. If it is one of the deterministic 422s (GRAPH_VALIDATION_FAILED, GRAPH_TOOL_NODES_NOT_SUPPORTED_IN_TEST_RUN, RESERVED_TOOL_NAME, INVALID_EXECUTOR_URL-family reject classes, INVALID_HANDOFF_TARGETS, INVALID_KNOWLEDGE_BASE_IDS) — do NOT retry; fix the payload, the graph, or the registry first.
  3. If it is TOOL_NAME_TAKEN or TOOL_DISABLED — clear the collision or re-enable the tool, then retry.
  4. If it is TOOL_IMPL_NOT_REGISTERED — treat as a runtime fault; do not retry and escalate.
  5. For GRAPH_VALIDATION_FAILED, scan error.details for the exact node/edge and fix on the canvas; the console prints the same list inline.
  6. If it is one of the runtime dispatch codes (CUSTOM_TOOL_TIMEOUT, CUSTOM_TOOL_NETWORK_ERROR, CUSTOM_TOOL_SERVER_ERROR, CUSTOM_TOOL_CLIENT_ERROR) — the executor owns the fault; follow Custom tool runtime failures and retry only after the executor changed.
  7. If it is CUSTOM_TOOL_DISPATCH_FAILED — platform side; a bounded retry is safe, then escalate with request_id.

What NOT to do

  • Do not retry GRAPH_VALIDATION_FAILED in a loop. The graph is invalid; a retry re-runs the same gate.
  • Do not rename a custom tool to work around TOOL_NAME_TAKEN. The name is the id the LLM sees and every attached agent trains prompts on it; renaming after the fact breaks trained prompts. Rename the duplicate target instead.
  • Do not point executor_url at an internal host with a public alias. DNS resolution runs at write time and again at dispatch, so a public-looking hostname that resolves internally still fails.
  • Do not escalate on TOOL_NAME_TAKEN / TOOL_DISABLED / INVALID_HANDOFF_TARGETS / INVALID_KNOWLEDGE_BASE_IDS / the 422 validation codes, or on the four executor-owned dispatch codes. These are deterministic tenant-owned gates. Escalate only TOOL_IMPL_NOT_REGISTERED (500) and CUSTOM_TOOL_DISPATCH_FAILED with request_id.
  • Do not retry the re-chunk on a legacy document. Re-upload through the canonical KB flow first; the 409 is deterministic.
  • Do not blind-retry the dispatch family. CUSTOM_TOOL_CLIENT_ERROR and CUSTOM_TOOL_SERVER_ERROR mean your executor rejected or crashed on the request; only CUSTOM_TOOL_DISPATCH_FAILED is safe to retry as-is.

When to escalate

Escalate to support only for TOOL_IMPL_NOT_REGISTERED (500) and CUSTOM_TOOL_DISPATCH_FAILED — the platform-side faults. The request_id from meta lets support trace which registry entry the executor resolved, or which dispatch attempt faulted. Everything else on this page is a deterministic tenant-side fix, including all four executor-owned dispatch codes.

See also