Troubleshooting
Where Polytoken keeps its files
Section titled “Where Polytoken keeps its files”Polytoken keeps its own state in three directories. The defaults follow the XDG Base Directory specification and are the same on every platform, including macOS.
| Contents | Default location | Redirect with |
|---|---|---|
| Configuration file, skills, facets, subagents, hooks, themes | $XDG_CONFIG_HOME/polytoken (default: ~/.config/polytoken) | POLYTOKEN_CONFIG_PATH |
| Sessions, logs, provider login state | $XDG_DATA_HOME/polytoken (default: ~/.local/share/polytoken) | POLYTOKEN_DATA_PATH |
| Model catalogs discovered from dynamic providers | $XDG_CACHE_HOME/polytoken (default: ~/.cache/polytoken) | POLYTOKEN_CACHE_PATH |
A redirect variable names the directory itself: Polytoken uses the value
exactly as written and does not append polytoken. With
POLYTOKEN_DATA_PATH=/tmp/pt, sessions land in /tmp/pt/sessions. An unset or
empty variable falls back to the matching $XDG_* value, then to the default
inside your home directory.
If POLYTOKEN_CACHE_PATH, XDG_CACHE_HOME, and HOME are all unset or
empty, Polytoken cannot resolve the cache directory for dynamic provider
catalogs. Polytoken then skips caching, logs a warning, and still discovers
the catalogs live from the provider.
The .polytoken/ directory at the root of a project is a different thing from
the global config directory. It holds project-scoped configuration only; see
Checking your configuration.
Reading the logs
Section titled “Reading the logs”The daemon’s log, the interface logs, and crash logs all land in
$XDG_DATA_HOME/polytoken/logs/ (default: ~/.local/share/polytoken/logs/).
ls -lt ~/.local/share/polytoken/logs/ | headThe path in this example is the default location; substitute it when you have
set $XDG_DATA_HOME.
Run logs carry a timestamp and a process ID, so each run gets its own file:
2026-09-05T13-22-04Z-368502.logis the daemon’s log for the run with process ID 368502. The daemon writes structured JSON entries to this file. A daemon that runs in the background also redirects its stdout and stderr into the same file, so startup failures and panics appear alongside the JSON entries.- The conversation interface writes logs ending in
-tui.log; the configurator writes logs ending in-configui.log. - A crash log ends in
-tui.crash.logand marks an involuntary exit. The file records the session ID, the exit reason, and, for panics, a backtrace. Normal exits write no crash log. When a panic or daemon attachment lease loss writes a crash log, the TUI announces the file path after restoring the terminal. A lease-loss message also includes the path when the write succeeds.
Two controls set verbosity, one per process. For the daemon’s log, set
daemon.log_level; its values are error, warn, info (the default),
debug, and trace. For the interface logs, set RUST_LOG to a standard
tracing filter, for example RUST_LOG=debug; the default is info. Polytoken
reports an error instead of launching when RUST_LOG holds an invalid filter.
Polytoken does not delete old log files. Runs accumulate until you remove the files yourself.
For non-interactive runs, polytoken exec --print-session-logs mirrors the
session’s JSONL events and the run’s trace log to stderr while the run
executes. That trace log, trace.jsonl, lives in the session directory rather
than the logs directory. See CLI.
Inspecting a session on disk
Section titled “Inspecting a session on disk”Each session owns a directory under $XDG_DATA_HOME/polytoken/sessions/,
named by its session ID. Three files answer most questions:
log.jsonlholds the session’s event log: every prompt, reply, tool call, and marker, in order. Polytoken replays this file to rebuild the conversation when you resume. See Sessions.session.jsonholds the metadata the session picker reads, including the project directory, the title, and timestamps.record.jsonexists only while a daemon owns the session. It records the daemon’s port and process ID; the recorded process ID is howpolytoken sessionsdecides a listed session is live.
The same directory holds todo records, shell-job output, subagent transcripts, and the exec trace log for the session. When daemon.logging.persist_system_prompts is on, Polytoken also creates system-prompts/ and stores one 0600 text sidecar for each changed composed system prompt. Polytoken keeps the directory at mode 0700. The sidecars help you compare provider prompts after a run and do not participate in session replay or exports.
Polytoken stores full output for capped tool results in the session’s tool-results/ directory and sets each spill file to mode 0600.
Native context records and exported artifacts
Section titled “Native context records and exported artifacts”A native compaction boundary stays in log.jsonl as a durable provider-state record. The record lets Polytoken resume, measure, and convert the context without reconstructing provider state from mutable files. A crash before the boundary or portable transition becomes durable leaves the old context active. A crash after the record becomes durable lets the committed record win after restart. A subagent whose model uses native compaction keeps the same kind of records in its own subagent-*.jsonl transcript, under the same rules.
The /history projection and the TUI expose method, fallback, occupancy, and non-secret timing metadata. They do not expose the provider’s opaque continuation state. Feedback artifact exports apply a redaction layer to native records, while the on-disk session log remains lossless for Polytoken’s own resume path. Treat the raw session directory as sensitive.
polytoken sessions lists live sessions and removes stale registry entries as
a side effect. Add --all to list every session directory on disk, including
finished ones. Legacy sessions that predate session.json appear in the
--all listing but cannot be resumed.
When Polytoken resumes a large legacy session, Polytoken shows live migration progress while it converts the session. Migration can take several minutes for a session with extensive history or many subagent logs. You can interrupt the resume safely. A later resume retries the conversion and converges on the migrated session.
Conflicting session trees
Section titled “Conflicting session trees”A conflicting-session-tree error means Polytoken found both the legacy session directory and the migrated session directory for one session ID. Polytoken does not choose one tree automatically because the two trees may contain different history.
Polytoken keeps the original legacy tree in sessions-migration/<id>/legacy-backup while migration remains recoverable. A recreated legacy directory that contains only startup scaffolding moves to sessions-migration/<id>/recreated-stub/ so Polytoken can retry without deleting the scaffolding. Polytoken never treats a legacy directory containing log.jsonl as scaffolding.
To recover, stop any process that owns the session and resume the session again. When the legacy directory lacks log.jsonl, or migration artifacts remain, another resume converges automatically. Polytoken archives the scaffolding stub and either publishes the staged history or restores the backup for another retry. When both the legacy log.jsonl and a valid migrated session directory contain history, another retry cannot resolve the conflict. Polytoken refuses to choose either tree and preserves both. Keep both trees unchanged and seek assistance.
Repairing out-of-order session history
Section titled “Repairing out-of-order session history”Use polytoken sessions repair <session-id> when Polytoken warns that v1
history is corrupt and a session fails to load, resume, or hand off, but every
line in log.jsonl remains valid JSON. This command repairs records whose
indexes no longer follow their physical order. It does not change record
content, and it saves the original log beside log.jsonl as
log.jsonl.corrupt-<UTC-timestamp> before replacing the log.
Stop the daemon that owns the session before running the command. Polytoken refuses to repair a session with a live owner or an owner record that it cannot read or verify. Run:
polytoken sessions repair <session-id>Polytoken refuses a missing session, a legacy session, any unparseable or torn line, and timestamps that move backward. Those problems are not index-order corruption and need a different recovery path. Polytoken validates the repaired log before reporting success; if the log is already valid, the command leaves it unchanged.
With --sessions-dir <dir>, Polytoken accepts either the sessions root the
daemon uses or the versioned <name>-v1 directory beside it. Polytoken
refuses a copied or moved v1 directory whose sibling directories are missing,
and the refusal names both accepted locations: repair takes the session
storage lock, and that lock lives beside the v1 data, so a relocated copy
cannot be repaired in place.
Checking your configuration
Section titled “Checking your configuration”Polytoken looks for a file named config.json, config.json5, config.toml,
config.yaml, or config.yml in the global config directory and in
.polytoken/ at the root of your project. A directory may hold exactly one of
them; two produce a load error naming both files. Project values override
global values, and a null in the project layer deletes a global key. Model-group
definitions use a different precedence rule: user/global definitions replace
project definitions, and project definitions replace shipped definitions. A
member of a model group cannot be null. Omit modelgroups.<name> to remove
that layer’s override and restore the lower-priority definition.
polytoken config validatechecks the merged result. On success, the first line readsmerged config valid. Add--useror--projectto check one layer alone; the first line then readsuser config valid: <path>orproject config valid: <path>and names the directory Polytoken checked. The second line starts withactive_session.default_facet (info):and reports the effective merged value when you check the merged result, or the value you configured in the selected layer. When the key is unset, the second line explains that startup falls back toexecute.polytoken config editopens the project layer in your editor by default, or the user layer with--user, and validates before saving: the real file changes only after validation passes.polytoken config uiopens the interactive configurator.
String values in the file can reference an environment variable with ${VAR},
the common way to supply an API key. A referenced variable that is not set
fails the load, and polytoken config validate names it.
polytoken doctor runs the same startup checks the daemon runs and prints
each result as a checklist line. The checks cover the startup surface:
configuration, dynamic models, model groups, providers and models, permissions,
shell behavior, hooks, skills, facets, subagents, tools, and MCP servers. The
model-groups check names skipped candidates when an authored route is degraded,
and reports the published-route count when every route is healthy. The command
exits non-zero when a check fails; MCP connection failures and partial model
groups appear as warnings. It starts no session and leaves nothing behind.
When a model-group warning appears, review its group and skipped leaves. Daemon logs also emit a WARN for each degraded or withheld group during startup and a successful reload. A fully broken authored group — every leaf confirmed unavailable — prevents config loading; its error names each unavailable leaf. A group whose leaves are all skipped but still pending a dynamic provider’s catalog is withheld with a warning instead. Check provider configuration, model names, and dynamic provider availability before trying the session again.
Environment variables
Section titled “Environment variables”Polytoken reads these variables when it starts.
| Variable | Effect |
|---|---|
POLYTOKEN_CONFIG_PATH | Uses this directory as the config root, without appending polytoken. |
POLYTOKEN_DATA_PATH | Uses this directory as the data root, without appending polytoken. |
POLYTOKEN_CACHE_PATH | Uses this directory as the cache root, without appending polytoken. |
XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOME | Standard XDG locations, consulted when the matching POLYTOKEN_*_PATH variable is unset. |
RUST_LOG | Verbosity of the interface log files; the default is info. |
EDITOR | Selects the editor for polytoken config edit; the default is vi. For the interface’s drop-to-editor command, a configured tui.editor key wins, then VISUAL, then EDITOR; the default is vim. |
POLYTOKEN_SKIP_UPDATE_CHECK | Set to any value to skip the launch-time update check. Polytoken sets this itself when it re-launches after an update. |
POLYTOKEN_DISABLE_FEEDBACK | Set to 1 to disable manual feedback, including /feedback and Ctrl+Shift+F. |
POLYTOKEN_FEEDBACK_DSN | Uses a custom destination for feedback reports instead of the default service. |
POLYTOKEN_TUI_COLOR_MODE | Forces truecolor or 256 output when terminal color detection is wrong. See Installation. |
POLYTOKEN_TUI_DISABLE_KEYBOARD_ENHANCEMENT | Set to 1 to skip requesting the terminal’s extended keyboard protocol. |
POLYTOKEN_DAEMON_CREDENTIAL_FILE | Environment form of the daemon’s --credential-file flag. See Daemon Authentication. |
POLYTOKEN_OUTPOST_URL | Default URL for Outpost management commands that accept --outpost-url. See Outpost. |
POLYTOKEN_OUTPOST_TOKEN | Token for Outpost commands and polytoken connect when --outpost-token is omitted. See Outpost. |
OTEL_* | OpenTelemetry exporter settings, when trace export is enabled. See Telemetry. |
Bash hook handlers receive their own set of POLYTOKEN_* variables describing
the session; see Hooks.
When a provider fails
Section titled “When a provider fails”Raise daemon.log_level to debug, reproduce the problem, and read the newest
daemon log.
For a durable record, turn on daemon.logging.provider_autopsy. Each provider
failure then produces a redacted report under autopsies/ inside the session
directory, without the request or response contents. The same directory also
holds diagnostics for tool calls that failed to parse, and those contain the
raw malformed input, so treat autopsies/ as sensitive. Polytoken prunes
older reports automatically.
Polytoken also writes a small summary line to the daemon log when each
provider request starts and finishes. Those lines carry counts and sizes
only, never request contents, and they are always on at the default log
level. For a detailed size and structure breakdown of every provider
request, including successful ones, turn on
daemon.logging.provider_request_diagnostics in your configuration file,
or use the Provider request diagnostics toggle in the configurator’s
Daemon pane. The key is off by default because the breakdown adds
preparation work to each request. Provider autopsy works independently of
this key: autopsy keeps gathering the detail failure reports need even
when the diagnostics key is off.
The /feedback command can attach the current TUI log tail and screenshots to a
report. Ctrl+Shift+F opens the same form. See
Crash Reporting and Feedback.
Goal Driver pauses after empty responses
Section titled “Goal Driver pauses after empty responses”When you set a goal, the Goal Driver keeps the work moving by sending continuation turns on its own. If a provider repeatedly returns empty responses, Polytoken pauses that automatic continuation and shows a reminder instead of asking the same model to continue. Check the provider or select another model before trying again. With a model group, Polytoken tries the next candidate after repeated empty responses; see Model groups and failover.
Use /goal resume to continue the goal with a fresh allowance of automatic
continuations. The same command grants a fresh allowance of automatic
continuations when the goal reaches its limit.
The assistant stalls in extended reasoning
Section titled “The assistant stalls in extended reasoning”Some models reason for a long time before they produce an answer. When a response keeps producing reasoning without any visible text or tool call, Polytoken interrupts the stream, asks the model to answer or act, and retries the response. These interruptions happen silently in the conversation view. Polytoken records each reminder in session history and replays it to the model on the retry. The retries keep a session usable when reasoning runs away.
The retry sequence is scoped to a stretch without progress. When the model resumes writing text that actually says something, or calls a tool, Polytoken discards the accumulated retries and starts fresh, so earlier interruptions do not weigh on later productive work. Blank text does not count: a response that emits only whitespace between reasoning bursts never resets the count, so the retry sequence keeps climbing until the response produces real output or the turn ends. A prompt you send after an interruption also starts fresh.
The retries are also bounded for your whole turn. When the same turn keeps falling back into extended reasoning, Polytoken counts every interruption and ends the turn after six of them, with the same visible note described below. From the second interruption onward you also see a warning card that reports how many times reasoning stalled in this turn, so a repeating stall is visible without opening the session log.
An interruption does not prove the model was repeating itself. Long, legitimate reasoning can trigger the interruption too. If retries still produce no answer or tool call, Polytoken ends the turn with a visible note that says the response was stuck in a reasoning loop. Try sending the request again, or switch to a model that reasons less.
A response sits with no visible progress
Section titled “A response sits with no visible progress”Polytoken watches every response stream on the wall clock, for the main conversation and for subagents. If a stream stops making progress (for example after your computer sleeps and wakes), Polytoken reconnects it through the same bounded retry ladder it uses for ordinary stream interruptions: the response is salvaged up to the last complete block and the request retries, with six consecutive stalls ending the turn with a visible error. In the main conversation, time spent running a tool and time spent in a provider rate-limit wait never count toward that ladder, so a long tool batch or a legitimate backoff does not trigger a reconnect. Subagents get the same protection while a rate-limit wait is in progress, so a visible stall marker does not appear while the provider backoff runs; a long subagent tool batch can still transiently show the stall marker. While a response is in flight, the status card names what the model is waiting for, including how long since the provider last sent anything, so a dead stream is visible before recovery fires.
When you cancel a turn yourself and the turn had not produced any visible output yet, Polytoken appends a short muted note that says you canceled the turn before it produced any output, so the transcript never ends silently on your prompt.
Checking language servers, themes, and definitions
Section titled “Checking language servers, themes, and definitions”Four read-only commands verify the pieces a session depends on: language servers, definitions, themes, and shipped resources.
polytoken lsp checklists every language server Polytoken knows and whether its binary resolves on this machine. Naming a server also runs its--versionprobe and reports the outcome as an informational note; the exit code reflects PATH resolution only. See Language Servers.polytoken validate skill <target>,polytoken validate facet <target>, andpolytoken validate subagent <target>parse and validate one definition file, by name or by path, using the same logic the daemon runs at startup.polytoken theme validatechecks a theme file against the token catalog. See Themes.polytoken vfs lsandpolytoken vfs catlist and print the resources Polytoken ships, such as the default theme. See The Polytoken VFS.