Skip to content

Troubleshooting

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.

ContentsDefault locationRedirect 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.

The daemon’s log, the interface logs, and crash logs all land in $XDG_DATA_HOME/polytoken/logs/ (default: ~/.local/share/polytoken/logs/).

Terminal window
ls -lt ~/.local/share/polytoken/logs/ | head

The 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.log is 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.log and 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.

Each session owns a directory under $XDG_DATA_HOME/polytoken/sessions/, named by its session ID. Three files answer most questions:

  • log.jsonl holds 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.json holds the metadata the session picker reads, including the project directory, the title, and timestamps.
  • record.json exists only while a daemon owns the session. It records the daemon’s port and process ID; the recorded process ID is how polytoken sessions decides 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.

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.

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 validate checks the merged result and prints the authored active_session.default_facet value. Add --user or --project to check one layer alone; each mode prints that layer’s authored value or explains that daemon startup falls back to execute when the key is unset.
  • polytoken config edit opens 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 ui opens 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, providers and models, permissions, shell behavior, hooks, skills, facets, subagents, tools, and MCP servers. The command exits non-zero when a check fails; MCP connection failures count as warnings. It starts no session and leaves nothing behind.

Polytoken reads these variables when it starts.

VariableEffect
POLYTOKEN_CONFIG_PATHUses this directory as the config root, without appending polytoken.
POLYTOKEN_DATA_PATHUses this directory as the data root, without appending polytoken.
POLYTOKEN_CACHE_PATHUses this directory as the cache root, without appending polytoken.
XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOMEStandard XDG locations, consulted when the matching POLYTOKEN_*_PATH variable is unset.
RUST_LOGVerbosity of the interface log files; the default is info.
EDITORSelects 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_CHECKSet to any value to skip the launch-time update check. Polytoken sets this itself when it re-launches after an update.
POLYTOKEN_DISABLE_FEEDBACKSet to 1 to disable manual feedback, including /feedback and Ctrl+Shift+F.
POLYTOKEN_TUI_COLOR_MODEForces truecolor or 256 output when terminal color detection is wrong. See Installation.
POLYTOKEN_TUI_DISABLE_KEYBOARD_ENHANCEMENTSet to 1 to skip requesting the terminal’s extended keyboard protocol.
POLYTOKEN_DAEMON_CREDENTIAL_FILEEnvironment form of the daemon’s --credential-file flag. See Daemon Authentication.
POLYTOKEN_OUTPOST_URLDefault URL for Outpost management commands that accept --outpost-url. See Outpost.
POLYTOKEN_OUTPOST_TOKENToken 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.

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.

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 provider stream, asks the model to answer or act, and retries the response. The retry keeps a session usable when reasoning runs away.

The retry sequence is scoped to a stretch without progress. When the model resumes writing text or calling a tool, Polytoken discards the accumulated retries and starts fresh, so earlier interruptions do not weigh on later productive work. A prompt you send after an interruption also starts fresh.

An interruption does not prove the model was repeating itself. Long, legitimate reasoning can trigger the interruption too. When Polytoken cannot get an answer or a tool call after several retries, it ends the turn with a note that says the response was stuck in a reasoning loop. Try sending the request again, or switch to a model that reasons less.

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 check lists every language server Polytoken knows and whether its binary resolves on this machine. Naming a server also runs its --version probe and reports the outcome in the exit code. See Language Servers.
  • polytoken validate skill <target>, polytoken validate facet <target>, and polytoken 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 validate checks a theme file against the token catalog. See Themes.
  • polytoken vfs ls and polytoken vfs cat list and print the resources Polytoken ships, such as the default theme. See The Polytoken VFS.