Troubleshooting#
Start with the exception and the last event you received. A tool error can be recoverable; a provider or lifecycle error ends the request. Use Integrate an application for failure boundaries and Test your agent to reproduce the behavior without a live model. Never include credentials or full private prompts in a public error report.
A decorated tool fails at import time#
A tool needs at least one non-empty guideline means the function has a
description but no usage guidance. Add @tool(guidelines="When to use it.")
or a nonempty Guidelines: docstring section. Every argument also needs a
type annotation. Args entries must match real parameter names; unknown
entries and unnamed variadic parameters are rejected. See Define and register tools.
The endpoint rejects reasoning or tool search#
The adapter does not negotiate model capabilities. Verify the endpoint supports
the selected protocol and reasoning level. On Responses, the current adapter
maps the highest levels to high; on Chat Completions an unsupported named
level may be rejected. OFF can omit the setting rather than disabling
provider-default reasoning.
ToolSearchExtension needs a compatible Responses tool-search endpoint,
not just ordinary tool calling. Use response=True and a model supporting
that protocol, or omit tool search and expose tools normally. Skill loading
does not require Responses. See Connect a model provider and Load tools and skills on demand.
The second turn forgot the first#
History comes from an extension. Passing extensions=[...] replaces the
defaults, so add InMemoryMessageAccumulator
or a persistence extension, and keep using the same session_id. Two clients
with different accumulator instances do not share memory just because the IDs
match.
The model never sees a message you added#
Add context in a lifecycle hook, not by editing shared state:
on_state— restore history and inject system instructions.on_message— replace the pending user input before it is sent.before_model— adjust context before each model step.
Use the context passed to the hook. See
The request lifecycle for exactly when each hook runs.
Your custom event never reaches the UI#
Internal notifications and UI events are different channels. context.publish
delivers an ExtensionEvent to extensions;
await context.emit(AgentEvent(...)) queues something for Agent.stream.
Use the second one for anything the user should see. See
Emit events and accept user input.
An approval stays pending#
The response must match the request’s session ID, external event name, and
payload shape. If no registered extension accepts it, the request keeps waiting.
Do not block the loop with input() — use an async UI or
asyncio.to_thread for terminal input. A cancelled request invalidates its
pending routes, so a late reply is ignored.
Reasoning or cache numbers are missing#
Reasoning text appears only when the model returns it; the effort level is a
request preference, not a promise. Usage is reported on MODEL_COMPLETED, not
on every text fragment. cache_hit_rate is None both when the provider
reported nothing and when there were no input tokens;
ModelUsage.cache_reported tells the two apart. Gateways that translate
protocols often drop the cache breakdown, so prefer a Responses adapter when
cache visibility matters.
The request just hangs#
Nothing is shown and nothing fails. The provider, or a gateway in front of it,
accepted the request and went silent. Every adapter now gives up and raises
ProviderTimeoutError; its phase says what
ran out: connect (nothing is listening), first_byte (the headers never came,
which is how an overloaded gateway looks) or idle (a response that had started
stopped). Test the endpoint directly with curl; if it hangs there too, the
fault is on the provider side and no setting here will help. See
the defaults and how to change them.
A request failed or was stopped and I want to continue it#
The conversation is saved up to the point it stopped. Call agent.resume(), or
agent.resume_point() first to see whether there is anything to resume and what
it will do. Do not send a “continue” message: it adds a user message the model did
not expect, and it does not tell the model that a tool was interrupted. See
Resume an interrupted session.
The error mentions a web page or an HTTP status#
answered HTTP 502 with a web page means something in front of the API, not the
API itself, replied: a proxy or gateway error, a firewall, or a login page. Check
base_url and the network path to it. returned HTTP 400: ... carries the
provider’s own explanation, such as an unknown model name; model ids can be
case-sensitive and a gateway may use its own. answered HTTP 200 with
application/json instead of an event stream usually means the endpoint does not
stream, or the base URL points at the wrong route.
Retries duplicated text#
Built-in retries stop after the first streamed event, so a retry should never
replay visible output. Check application-level retries and make sure you are not
appending both the final message text and every TEXT_DELTA fragment to the
same buffer.
Project instructions or a skill did not appear#
AgentsMdExtension loads the chosen directory and its ancestors, not every
nested file in the workspace. Check sources() and instructions(), and
verify the extension is in your explicit list. Empty or unreadable files are
skipped. directory does not change tool paths. See Load project instructions.
For skills, check extension.skills. Discovery occurs at construction time;
new files or catalog metadata need a new extension. Existing skill bodies are
read on demand, but editing a file does not replace instructions already in
history. Pass explicit project roots rather than assuming defaults include
the working directory. See Load tools and skills on demand.
An MCP server has no tools or fails before generation#
Check extension.servers and extension.config_path. A missing config
loads no servers, whereas a bad config raises. Discovery connects on every
request; a server that cannot be reached fails setup rather than silently
disappearing. By default a remote search tool on catalog is named
catalog__search. See Connect MCP tools for transports and namespaces.
The runtime rejects a second request#
Two requests for the same session cannot overlap. Queue them or finish/close
the first stream, including cleanup. Breaking out of a bare async iterator
without aclosing can leave unfinished work. Different session IDs may run
concurrently, but separate runtimes and workers do not share an admission lock.
See Integrate an application.
Compaction did not run#
It needs a crossed size threshold and an older completed turn it can replace.
The current turn is kept whole, so a single oversized turn is left alone. When
the generated summary would not be smaller than the messages it replaces,
COMPACTION_COMPLETED reports applied=False. See
Compact without losing history for a reproducible test.