Skip to content

Troubleshooting ​

What goes wrong in practice, the message noeta prints when it does, and what to change. Every message on this page is quoted from the code; search the repository for a phrase to find where it comes from.

Where to look first ​

  • The command's own error. The noeta command exits with one line starting noeta: for anything it can name (no model, no key, a refused flag). Read that line before anything else. Anything else prints noeta: unexpected error: <type>: <message> and the path of a crash report under ~/.noeta/logs/ (the traceback, the version, the command line); attach that file to a bug report. --verbose or NOETA_DEBUG=1 prints the traceback on the terminal too.
  • The task's end or turn failure. A turn that cannot go on records a TurnFailed with a reason. An interactive task keeps its conversation and waits for the next message; a one-shot task (any sub-agent, or an agent file with one_shot: true) ends with TaskEnded(status="failed", error=…), and error starts with the reason. The reasons are listed in Turn failures.
  • The worker's standard error. noeta configures no logging of its own, so Python's default applies: warnings and errors from noeta worker and noeta serve go to standard error. Run them under a service manager that keeps it.
  • noeta plugins lists every plugin with its source, and every plugin file that failed to load, marked (failed), with the reason.
  • /mcp in the terminal UI shows each MCP server as connected, waiting for a sign-in, or failed with its error.

Starting up ​

You seeCauseFix
noeta: no model configured (--model or NOETA_MODEL)No setting names a model.Pass --model, set NOETA_MODEL, or set model in ~/.noeta/config.toml (configuration).
noeta: no API key for the anthropic provider: set NOETA_API_KEY (ANTHROPIC_API_KEY is read too), or api_key in ~/.noeta/config.tomlThe default provider has no key.Set one of the named variables, or api_key (a literal or a {env:NAME} / {keyring:service/user} reference).
…/config.toml: unknown top-level keys ['modle']; ignoredA top-level key noeta does not know, often a typo; the setting it meant stays unset.Fix the key's name (configuration keys).
no API key for provider 'x': set api_key in its [providers.x] table of …/config.tomlA [providers.<name>] table has no key.Add api_key to that table (a {env:NAME} or {keyring:service/user} reference works).
…: {env:NAME} names an environment variable that is not set / …: the keyring has no secret for 'service/user' / …: {keyring:…} needs the keyring packageA secret reference cannot be filled on this machine. References are filled only when the plugin or provider is made, so this shows when it is first used.Set the variable, store the secret (keyring set service user), or install the keyring extra.
noeta: this needs the server extra, which is not installed: pip install '…' (also postgres, acp, keyring)noeta serve, a Postgres store, noeta acp or keyring secrets need an optional extra.Run the printed line. With uv tool install --reinstall, name every extra you use: a reinstall drops the ones it does not name.
noeta: the terminal UI is its own package, noeta-tui, which is not installed: …noeta with no subcommand starts the terminal UI, which is the separate noeta-tui package.Run the printed install line, or use noeta -p for one prompt.

noeta service and noeta update ​

You seeCauseFix
noeta: API (no web app) at http://… when noeta serve startsThe installed package was built without the web app: a checkout, or a wheel built without npm. The API works.In the checkout run make web-bundle (it needs npm), or install noeta from PyPI.
noeta service: no systemd user instance or launchd here to run a service; …No systemctl --user session (a container, a login without a user manager) and not macOS.Run noeta serve in tmux (tmux new -d -s noeta 'noeta serve').
noeta service: not installed (noeta service install [serve flags])status, logs, start, stop, restart or uninstall with no service installed.noeta service install first.
noeta service: lingering is off for <user>, so the service stops when you log out; …Linux: the user's systemd instance ends with the last session.Run the printed loginctl enable-linger <user> (it may need sudo).
noeta service: systemctl --user restart noeta.service: … (failed in noeta service status)The service did not start: no model configured for it, a port in use, a store it cannot open.noeta service logs --no-follow says why; fix it, then noeta service install again (the environment is copied at install time) or noeta service restart.
noeta update: cannot reach PyPI to check for a new version (…); nothing changedNo network, or a proxy noeta does not know about.Set HTTPS_PROXY (or ALL_PROXY) and run it again.
noeta update: this noeta (…) runs from source …, so nothing is changednoeta runs from a checkout or an editable install, which noeta update leaves alone.Update the checkout, or switch to the release with the command it prints.
noeta update: could not restart noeta service; run noeta service restartThe upgrade went through; restarting the installed service failed.noeta service restart, then noeta service logs --no-follow if it fails again.
noeta update: cannot reach PyPI to check for a new version (…); nothing changedNo network, or a proxy noeta does not know about.Set HTTPS_PROXY (or ALL_PROXY) and run it again.
noeta update: this noeta (…) runs from source …, so nothing is changednoeta runs from a checkout or an editable install, which noeta update leaves alone.Update the checkout, or switch to the release with the command it prints.

Models and providers ​

noeta maps every provider failure onto a few classes (noeta/core/errors.py) and retries the ones worth retrying. A retry is recorded in the log and shown by front ends as "retrying".

ClassWhenWhat noeta does
Transientnetwork errors, timeouts, HTTP 5xx (including Anthropic's 529 overloaded), a stream that broke after it beganRetries with backoff (doubling from 1 s, capped at 30 s, half of it random), up to 9 attempts in all (DEFAULT_MAX_ATTEMPTS, noeta/core/retry.py). A broken stream may be sent again in one piece.
RateLimitedHTTP 429Retries like Transient; when the provider sends Retry-After, it waits exactly that long.
ContextOverflowa 413, or a 4xx whose body says the prompt is too longNot retried as is. When the provider said the input fits and only the output cap does not, the call is made again with a smaller cap; otherwise compaction makes room (below).
Fatalany other 4xx: a bad key, a bad request, a refusalNot retried. The turn fails with llm_error and the provider's message, its body cut to 500 characters.

After the retries run out the turn fails with llm_error: <message> — <body>. Read the body: it is the provider's own words.

Messages that point at configuration rather than at the provider:

  • provider 'x' cannot be used: … — the adapter for a [providers.x] table could not be built (no key, a bad setting). Fix the table.
  • this task runs 'model' on provider 'x', which is not configured here (providers: …) — with several providers, a task records which provider its model goes to (model_route), and every worker sends it there. This worker's config.toml has no table of that name. Give every worker the same [providers.*] tables.
  • request carries an image but model 'm' is catalogued with supports_vision=False; refusing to send the image to a model that cannot read it. — the catalog says the model has no vision. Switch models for that message.
  • this provider does not support image input outside a plain user turn; refusing to silently drop the image. — an OpenAI-compatible endpoint got an image where its wire has no place for one.
  • the model call kept being interrupted (with llm_error) — a model call was started three times without a reply being recorded (MAX_MODEL_ATTEMPTS): the worker died mid-call each time. Look at why the workers die (memory, a supervisor killing them).

A model the catalog does not know gets a 128,000-token window and a 16,384-token output cap, with a warning. If compaction starts too early or too late for a gateway model, give its limits in a models file or the provider's table (models).

Context overflow and compaction ​

When a request does not fit, the compaction plugin summarizes the conversation and the call is made again. You only see a failure when that cannot work:

ReasonMessageWhat to do
context_overflowthe provider's overflow textNo plugin could make room (compaction is off, or the agent does not run it). Turn compaction on, or start a new task.
autocompact_thrashingAutomatic compaction stopped: the context filled up again within 3 model calls of the previous compaction, 3 times in a row. What a request still carries after a summary (the system prompt and tools, project instructions, files read again, the newest tool output) does not fit the model's context window. Read large files in smaller parts, check the model's context window is registered right, or start a new conversation.What stays after a summary leaves no room: one file or output is close to the whole window, or the model's real window is smaller than its catalog row (an uncatalogued model is taken as 128K). Ask for large files in parts, or register the model's real limits.
compaction_summary_failedwhy the summary call failedThe summarize call itself failed (it is retried up to three times when it is too long). After three failed automatic compactions in a row, automatic compaction is off for the task; /compact still works.
compaction_config_invalidthe bad valueautoCompactWindow or NOETA_AUTOCOMPACT_PCT_OVERRIDE is not usable for this model. Fix the agent file or settings.

See context, caching and compaction for the mechanism.

Turn failures ​

The reasons a turn fails with (noeta/core/runner/texts.py and the plugins that raise TurnFailed). Front ends show them; hosts branch on the prefix.

ReasonMeaning
llm_errorThe provider failed after retries, or rejected the request.
llm_empty_responseThe model returned no text and no tool call.
llm_truncatedthe response kept hitting the output token limit: three output-cap recoveries in a row. Raise the model's output cap or ask for smaller steps.
context_overflow, autocompact_thrashing, compaction_summary_failed, compaction_config_invalidSee above.
max_turnsReached maximum number of turns (n): the task's max_turns (--max-turns) was reached.
needs_humanthe sub-agent stopped without completing (it needed an interaction that is unavailable to a background agent): a task a host marked unattended reached an approval or a question. Give it permission rules that cover what it does, or do not mark it unattended.
blocked_by_hook, stopped_by_hookA UserPromptSubmit hook blocked the prompt, or a hook stopped the turn. The detail is the hook's reason.
max_structured_output_retriesWith an output schema, the model failed to produce a valid StructuredOutput answer within the retry cap.
internal_errorA bug in the runner, a plugin hook or a provider adapter. A second one in a row ends the task. The worker's standard error has the traceback.

A task that stops at its step limit is not a failure: the model is told You have reached the limit of {n} steps for this task… and its last answer starts Stopped at the {n}-step limit before finishing.

A tool call says "outcome unknown" ​

Interrupted before its result was recorded, so its outcome is unknown — it may have fully,
partly, or not applied. Check its effect before running it again.

The worker that ran the call died (or lost its claim) after logging ToolStarted and before logging the result. noeta never runs such a call again by itself unless the tool can look its outcome up (Tool.recover) or says it is safe to repeat (retry_safe). The model reads this text and usually checks. Nothing needs fixing unless workers die often (recovery).

A background Bash job whose worker died is reported as Background command "…" was lost: the worker process running it stopped, so its further output and exit status are unknown (Monitor "…" was lost: … for a monitor). A job lives in one worker process; there is no way to reattach to it.

Plugins ​

You seeCauseFix
(failed) <path>: <error>; install it next to noeta: uv tool install --reinstall --with <module> '…' (from noeta plugins or /reload-plugins)A plugin file imports a module noeta's environment lacks. noeta plugin add installs no dependencies on purpose.Run the printed line, naming every extra and --with you already use.
plugin file <path>: 'name' joins no agent; set add_to = ("main",) on its class, or [agents.main] add_plugins = ["name"] in config.tomlThe plugin loaded, but no agent runs it.Do one of the two things the notice says (write a plugin).
<project>/.noeta/plugins was not loaded: this project is not trusted (run noeta trust in <project> to load its plugins)A project's .noeta/plugins, .noeta/agents, .noeta/prompts, .noeta/skills and .agents/skills load only once you trust it.Run noeta trust in the project, or answer the terminal UI's trust question.
A plugin never appears, with no errorThe machine's /etc/noeta/config.toml [policy] blocks it (blocked_plugins), or allowed_plugins is set and does not name it (it limits only plugins noeta does not ship). The policy wins over every user and project setting.Ask whoever manages the machine.
A task ends with plugin_missing: this task runs with <names>, which the worker that claimed it does not have; install them there (or run the task where they are)See below.Install the plugin on every worker.

A worker without a plugin the task needs ​

A task records which plugins it runs with when it starts (records.Plugins). A worker that lacks one of them never runs the task without it: it writes nothing, logs task …: runs with …, which this worker does not have; leaving it to a worker that has them, and lets its lease run out so another worker can claim the task. If the same worker claims it three times in a row (LOAD_ATTEMPTS) with nothing new in the log, it ends the task failed with plugin_missing.

A task pinned to a plugin this worker lacks Worker A house_style not installed Store pin: fs · shell · house_style Worker B house_style installed claim claim Runs the task folds the log and goes on whichever claims next On Worker A, with nothing new in the log claim 1 writes nothing claim 2 writes nothing claim 3 gives up lease ends lease ends TaskEnded failed plugin_missing: … install them there

This is what a rolling install looks like when it takes too long, or when one machine was forgotten. Ship the same plugins (and the same ~/.noeta) to every worker (run it as a server). The count is kept in each worker's memory, so a worker restart starts it again.

A task's log that no claim can read or fold (three claims in a row) ends with unreadable_log: the task's log could not be read on 3 claims in a row; last error: …. A log written by a newer noeta is not counted: an older worker leaves it alone for a newer worker.

The store ​

You seeCauseFix
schema 'noeta' is at version N; this noeta knows M (stop every worker and upgrade) (Postgres), sqlite store <path> is at schema version N; …A newer noeta already moved the store forward. An older process refuses to open it.Upgrade this machine. Workers of different versions must not share a store: stop them all, upgrade, start them.
cannot open the postgres store: …, no postgres connection: …, postgres connection lost: …Postgres is unreachable.Check NOETA_STORE and the network. Nothing was written; the call can be made again.

A write whose connection dropped while it was committing may or may not have landed (CommitUnknown). The worker stops running that task as if it had lost its claim; whoever claims it next reads the log. You may see the task pause for one lease (30 seconds by default).

The server and its clients ​

You seeCauseFix
noeta: the server at <url> needs its bearer token: set NOETA_SERVER_TOKEN to the token in its …/server-token (the #token= of the address it prints)The client has no token: it runs on another machine, or with another NOETA_HOME.Set NOETA_SERVER_TOKEN on the client to the server's token (noeta service status or the server's start output shows it).
noeta: the server at <url> refused the token: … / HTTP 401 {"error": "unauthorized"}Wrong token, or an old one: the server's token file was deleted, or it runs with its own NOETA_SERVER_TOKEN.Take the token from the address the server printed at its last start (or noeta service status).
HTTP 403 {"error": "cross-site request refused"}The Host header is not one the server answers (the loopback names, this machine's own names and addresses on a bind that is not loopback, and --allow-host names), or a browser page from another origin sent the request.Add --allow-host NAME for the name clients use (a reverse proxy's), or --allow-origin ORIGIN for a page served elsewhere.
noeta: --no-token serves only a loopback address--no-token with --host other than loopback.Keep the token, or bind to 127.0.0.1.
this server does not allow bypassPermissions (it runs with --disable-bypass-permissions, or its settings' disableBypassPermissionsMode is "disable")The server was started with --disable-bypass-permissions, or a settings file it reads sets disableBypassPermissionsMode. In a running task the switch is refused with the notice bypassPermissions is disabled here.Ask for another mode, or start every worker and server without --disable-bypass-permissions and that setting.
noeta: --permission-mode bypassPermissions contradicts --disable-bypass-permissionsnoeta serve or noeta worker was given both.Drop one of them.
HTTP 409 task … is not waiting on a request '…' (unknown, or already answered) on POST /tasks/{id}/answersThe request_id is a typo, from a stale card, another task's, or already answered and taken.Read GET /tasks/{id}/pending again and answer what is there.
A /stream-json WebSocket closed with code 4401 / 44034401: no valid token. 4403: the permission mode is refused, or the cwd asked for is no directory; the close reason says why.Pass the token (?token= or the header); ask for an allowed mode or an existing directory.
HTTP 400 cwd '…' does not exist (…) / cwd '…' is not a directory (…)A task, project or folder listing named a path on the server that is not a directory (a relative one is taken from the server's directory).Name an existing directory on the server's machine.
a request may not set the task config key 'x' (only model, effort, …)Only some config keys may come from a client; the rest would loosen permission gates.Set the key in the server's own config or agent file.
the request failed on the server; its log has the detailsAn error raised outside noeta's own code. Its text is not sent, since it may name server paths.Read the server's standard error.
noeta: -p runs its task here; --server attaches the terminal UI (or noeta acp --server) to a server-p always runs locally.Drop --server, or use the terminal UI or noeta acp --server.
noeta: --plugin-dir, … apply to the tasks this process runs; with --server the server's settings and plugins runA remote task runs with the server's plugins, permission rules, settings and MCP servers.Set them on the server, or drop --server.

Live text not appearing across machines while recorded events do: live output goes through Postgres notifications (<schema>_deltas), best effort and never stored. A front end that attached after a reply began sees the recorded reply when it lands. On sqlite, live text stays in the process running the task.

MCP servers ​

/mcp lists every server: ✔ name · connected · N tools, △ name · needs sign-in, or ✘ name · failed: <error>. noeta mcp list checks them from the command line.

You seeCauseFix
noeta mcp list shows project not trusted (not started; run `noeta trust` here)A local or project server of a project not yet trusted; it is not started, not even to check it.Run noeta trust in the project.
A project's server in .noeta/mcp.json never startsA project's servers start only in a trusted project and with consent in its own settings. noeta mcp add says: It starts once you consent: add 'name' to enabledMcpjsonServers in ….Run noeta trust, then add the name to enabledMcpjsonServers (or set enableAllProjectMcpServers: true) in .noeta/settings.local.json.
MCP tool name 'x' is produced by both server 'a' and server 'b'; rename one aliasTwo servers produce one tool name.Rename one server in your config.
MCP server 'x' needs exactly one of command and urlA config entry has both or neither.Fix the entry.
spawn failed: …A stdio server's command could not start.Check command and PATH on the worker.
The 'x' MCP server needs a sign-in. … (to the model), △ x · needs sign-inAn HTTP server wants OAuth.noeta mcp login x, or follow the link the model gives you.
A server's tools are missing from requestsMCP tools are deferred: the model loads them with ToolSearch when it needs them. A tool whose name passes 128 characters is never offered (logged once per listing).Set alwaysLoad: true on the server to send its tools from the first request.
A call or listing times outListing, prompts and resources have 30 s (NOETA_MCP_TIMEOUT, ms); tool calls have no practical limit unless the server's timeout (ms) or NOETA_MCP_TOOL_TIMEOUT sets one.Set the server's timeout or the variable.
No MCP tools at all from an SDK host using default_agentsThe built-in agents keep MCP and memory off until a setting names them (OFF_UNLESS_SET, noeta/agents/__init__.py). The noeta command sets them for you.Pass a setting for mcp (plugins={"mcp": {...}}).

More in MCP servers.

The sandbox ​

You seeCauseFix
sandbox.enabled is set but the sandbox cannot start: bubblewrap (bwrap) is not installed; install it (apt install bubblewrap, dnf install bubblewrap) to run commands in the sandbox. Commands run without it.Linux without bwrap. With failIfUnavailable the tail is Commands that would run in it are refused (failIfUnavailable).Install bubblewrap.
… bwrap cannot create the sandbox here: …bwrap exists but cannot create namespaces: a container without user namespaces, or a kernel setting that forbids them.Allow unprivileged user namespaces, or run noeta outside that container.
… the sandbox's system-call filter (refusing Unix sockets and new user namespaces) does not support the <arch> architectureThe filter knows only x86_64 and aarch64.Set both sandbox.network.allowAllUnixSockets and sandbox.allowNestedUserNamespaces (the filter is then not needed), or turn the sandbox off on that machine.
the sandbox is required (sandbox.failIfUnavailable) but cannot start: <reason>. The command was not run, and running it outside the sandbox (dangerouslyDisableSandbox) is refused too. (to the model)failIfUnavailable is on and the sandbox cannot start.Fix the reason above.
the sandbox does not allow connecting to host:port; add it to sandbox.network.allowedDomains to watch this socketA Monitor WebSocket to a host the sandbox does not allow.Add the host to sandbox.network.allowedDomains.

Without failIfUnavailable, a sandbox that cannot start leaves commands running unconfined, with the notice above at start. See where commands run.

Optional tools that do not appear ​

  • WebSearch is offered only with a search backend key, NOETA_WEB_SEARCH_API_KEY (Tavily).
  • Workflow runs scripts in an embedded V8 from the mini-racer package, which comes with the workflow extra (pip install 'noeta[workflow]'). Without it the tool refuses with the mini-racer package is not installed (…) and the install line, or mini-racer's V8 library does not load here (…), and starts nothing; noeta plugins names the missing extra too.
  • The task tools (TaskCreate and the rest) are offered only to the models Claude Code gives them to, unless [plugins.todos] all_models = true.