Troubleshooting
The commands on this page use the installed oat command. A few entries also
give a pnpm run ... form; those work only inside a checkout of the OAT source
repository and are labelled that way.
Skills not visible in host UI
A skill shows up in your coding tool only when two things are true: the skill
is installed (a folder with a SKILL.md under .agents/skills/ in this
repository, or under ~/.agents/skills/ in your home directory), and the
tool can read it. Some tools, such as Claude Code, read a provider view: a
link or copy that OAT creates in the tool's own folder, such as
.claude/skills. Others, such as Cursor, read .agents/skills directly.
Diagnose first. These commands only read; they change nothing.
-
Check whether the skill is installed, and where:
oat tools info <skill-name>It prints the skill's pack, its scope (
projectfor this repository,userfor your home directory) and its status, then a "Provider views" block for each scope with one row per coding tool.Tool '<skill-name>' not found.means the skill is not installed at either scope. Install the pack that contains it (see the fixes below), or create the skill at.agents/skills/<skill-name>/SKILL.md. To see every installed skill with its pack and scope, runoat tools list.in-syncon your tool's row means its view of the skill is in place, so go to the last fix step and start a new session.missing-additive,removedormodifiedmeans your tool's view is absent or out of date. The block ends with aRepair:line that gives the exactoat sync --scope ...command to run.inactivemeans that tool is not active at that scope, so OAT creates no view for it. Enable it, as shown in the fixes below.
-
Check whether the repository's provider views are in sync:
oat status --scope projectEach row names a tool and a skill.
✓ in_syncmeans the view is in place.✗ missing provider entry missingmeans the view has not been created yet;oat synccreates it.No managed entries found.means OAT manages no provider views in this repository, which usually means no tool that needs views is enabled. Runoat providers list --scope projectto see which tools are enabled or detected.
Then fix, always with an explicit scope:
-
Enable the tool if step 1 or 2 showed it is not active, for example
oat providers set --scope project --enabled claude. -
For skills in this repository (
.agents/skills), preview and then regenerate the provider views:oat sync --scope project --dry-run oat sync --scope project -
For skills installed at user scope (
~/.agents/skills, where a tool pack installs when you give no--scope), runoat sync --scope user. This writes provider views in your home directory, such as~/.claude/skills. -
If the skill comes from a pack that is not installed, install that pack with an explicit scope, for example
oat tools install research --scope project(this repository) oroat tools install research --scope user(your home directory). If you installed a pack with--no-sync, running the install again does not create the views; runoat syncfor that scope instead. -
Reload the tool or start a new session: most tools read their skill list when a session starts.
-
If your repository's
AGENTS.mdnames skills or packs, check that it matches whatoat tools listreports. The<!-- OAT tools -->block that--project-guidancewrites lists the installed packs and the folders agents should scan for skills; it does not list each skill. -
If you are working in the OAT source repository: run
pnpm run cli -- sync --scope all, which runs the CLI from source and syncs both project and user scope.
Worktree checkout missing provider links
- In the new worktree, run
oat sync --scope projectto recreate the project's provider views. Add--dry-runfirst to preview. - To prepare the whole worktree, including local-only files and the
repository's setup and readiness checks, invoke
/oat-worktree-bootstrap --existingin your coding agent. See Worktree bootstrap. - If you are working in the OAT source repository: run
pnpm run worktree:init. This command copies local-only files from the main worktree, installs dependencies, configures Git hooks, builds the workspace, and runsoat syncwith no scope flag. A bareoat syncuses--scope all, so it refreshes user-scope provider views in your home directory as well as the project's.
Codex appears detected but no skill sync actions are listed
Expected for native-read skill mappings. Codex can read canonical skills without mirrored provider writes.
Cursor appears detected but no skill sync actions are listed
Expected for native-read skill mappings. Verify project skills under
.agents/skills and personal skills under ~/.agents/skills; Cursor reads
those canonical directories directly. Sync will not create .cursor/skills
mirrors.
Use .cursor/skills only for intentionally Cursor-specific packages. Interactive
oat init and oat status offer each unresolved Cursor-local skill for
canonical adoption or a remembered Keep Cursor-only choice.
Copilot appears detected but no skill sync actions are listed
Expected for native-read skill mappings. Verify project skills under
.agents/skills and personal skills under ~/.agents/skills; Copilot reads
those canonical directories directly. Sync will not create .github/skills or
~/.copilot/skills mirrors.
Legacy skills in those provider directories remain adoption candidates. Run
oat sync --scope <project|user|all> --dry-run before upgrading: verified clean
managed views may be removed, while changed or unverifiable paths are preserved
and detached from obsolete manifest ownership. Copilot agents and project rules
continue to sync to .github/agents, ~/.copilot/agents, and
.github/instructions.
Interactive oat init and oat status offer each unresolved legacy skill for
canonical adoption or a remembered Keep Copilot-only choice. Keep is blocked
when a same-name canonical skill exists; rename one package before retrying.
sync reports provider config mismatch
- For interactive runs, select detected providers to enable when prompted.
- For non-interactive runs, configure providers explicitly:
oat providers set --scope project --enabled <providers> --disabled <providers>
- Re-run
oat sync --scope projectafter updating config.
instructions validate reports missing, content_mismatch, stray, or managed_shim
- Check the effective strategy (
strategy:in the output). The default isnone, which expects noCLAUDE.md;missingis only reported under a configured shim strategy. - Run
oat instructions sync --dry-runto preview changes. - Under
none,managed_shimentries areCLAUDE.mdfiles OAT created;oat instructions syncremoves them, unless aCLAUDE.mdwith its own content blocks removal (see below). - To keep shims instead, run
oat config set instructions.claude.shims pointer(orsymlink/copy) and thenoat instructions sync. - Under a shim strategy, if mismatched
CLAUDE.mdfiles should be overwritten, runoat instructions sync --force. - If
strayis reported,oat instructions syncwill adopt the Claude-only file intoAGENTS.md, then regenerateCLAUDE.mdunder a shim strategy or remove it undernone.
instructions sync removed no shims because a CLAUDE.md has content
- Under
none, removal is all or nothing: while anyCLAUDE.md,.claude/CLAUDE.md, orCLAUDE.local.mdhas content of its own, sync removes noCLAUDE.mdat all, reports each held-back removal as skipped, and exits1. The finding (claude_md_blocks_shim_removalunder--json) names the files with content (paths) and theCLAUDE.mdfiles kept (wouldRemove);linkedBynames anyAGENTS.mdlink to a file with content, which you replace with that file's content before removing it.oat instructions validatereports the same finding. - Either remove each named file or move its content into an
AGENTS.md, then rerunoat instructions sync; or setinstructions.claude.shimstopointer,symlink, orcopyto keepCLAUDE.mdfiles. - See Instruction Sync.
instructions sync warns that a CLAUDE.md makes Claude Code ignore every AGENTS.md
- Under the default
nonestrategy, a remainingCLAUDE.md,.claude/CLAUDE.md, orCLAUDE.local.mdmakes Claude Code'sagents-mdplugin stand down: for every session when the file is at the repository root, and for sessions started in its directory or below when it is in a subdirectory. - Either remove the named file, or set
instructions.claude.shimsto a shim strategy and rerunoat instructions syncto add shims everywhere. - If the warning names an
AGENTS.mdthat links to the file (linkedByunder--json), that file holds the only copy of those instructions: replace each linkingAGENTS.mdwith the file's content first, then remove the file. - Sync never deletes a hand-written or modified
CLAUDE.mditself; see Instruction Sync. - If a broken or unreadable instruction path is reported, fix the underlying file or symlink target first; sync will intentionally skip manual-repair cases instead of forcing recovery.
- If a directory you expected to see is missing from the scan, confirm it is not under
.git,.oat,.worktrees, ornode_modules. - Re-run
oat instructions validateand confirm status isok.
Use Instruction Sync for the full strategy matrix and state model.
doctor warns about canonical directories
- Run
oat initfor the relevant scope. The check's name says which scope is missing directories: forproject:canonical_directoriesrunoat init --scope project, and foruser:canonical_directoriesrunoat init --scope user. A bareoat initinitializes both. - Re-run
oat doctorafter initialization.
doctor warns about outdated installed OAT skills
- Run
oat tools update --pack <pack> --scope <project|user>to update an installed pack, oroat tools install <pack> --scope <project|user>to install one. The older formoat init toolsis a legacy command that still installs or updates the bundled tool packs. - In TTY mode, select which outdated skills to update when prompted.
- In non-interactive mode, run the scoped command doctor prints:
oat tools update --pack <pack> --scope <scope>. - Per-pack install commands no longer accept the previously inert
--forceoption.
A skill fails with unknown command 'template' or 'closeout-check'
Lifecycle skills from OAT 0.3.11 copy their templates with
oat template resolve and check closeout with oat project closeout-check.
An older oat on PATH rejects both:
error: unknown command 'template'
error: unknown command 'closeout-check'oat-project-complete treats a failed closeout check as an incomplete
closeout, so with an older CLI it stops and routes back to
oat-project-implement even when every closeout step is done. This happens
when the skills a host loads are newer than the CLI, for example repository
skills from a newer checkout run against an older global CLI.
Check oat --version. Install @open-agent-toolkit/cli 0.3.11 or later, then
refresh the installed skills to that CLI's bundle with
oat tools update --all.
sync reports an unsafe provider parent
Errors containing Unsafe provider parent, symbolic links are not allowed in provider ancestry, or provider ancestry is not a directory mean an existing
parent of a managed provider destination is a symlink or a non-directory entry.
OAT refuses to traverse, unlink, or rewrite that parent because it may be
user-managed or externally owned. Canonical content and external symlink targets
remain untouched.
Recover explicitly:
-
Inspect the reported parent and preserve or migrate any user-managed content.
-
Replace that provider parent with a real directory under the intended project or user sync scope.
-
Rerun sync with the matching scope:
oat sync --scope project oat sync --scope user oat sync --scope all
Do not replace the parent until you understand who owns its existing target. OAT intentionally does not automate this recovery.
A user-installed pack is missing from shared tools.*
This is expected. .oat/config.json#tools records project-scope intent
only. A user install writes tools.<pack>: true to ~/.oat/config.json
instead, and never touches repository config. Since every reusable pack now
defaults to user scope on a fresh install, an empty repository tools map is
normal.
Check effective project-plus-user availability with:
oat tools has <pack>To confirm specifically that the user copy is present, run:
oat tools has <pack> --scope useroat tools has <pack> reports false for a pack I installed
Availability is complete-only: a scope counts only when every managed asset the
current release declares for it is present. A partially installed pack — for
example one missing a skill added in a newer release — reports false.
Read the reason from the JSON result and repair the named scope:
oat --json tools has <pack>
oat tools update --pack <pack> --scope <project|user>completeness shows complete, partial, or absent per scope, and missing
lists the exact managed assets and paths behind a false.
status or doctor reports a partial or stale pack
Both commands report managed pack state with a scoped recovery command:
partial— some managed assets are missing, or intent is declared with none installed. Fix withoat tools update --pack <pack> --scope <scope>.stale— installed assets are behind the bundled release, or a managed skill or agent has the bundled version metadata but different canonical content. Expected executable-bit normalization on managed scripts is ignored. Fix with the same scoped update command.newer— installed assets are ahead of the bundle. This is informational and usually means the CLI is older than the assets; update the CLI rather than downgrading the pack.retained-override— a source-backed owner-owned seed differs from its bundled default, so OAT retains the edited copy instead of overwriting it. A bundle-equal seed is current and does not produce this finding. Retained overrides cover repository templates under.oat/templates/and seeded content such as.oat/ideas/backlog.mdand.oat/ideas/scratchpad.md. Deleting the file is the right move only for a template you intentionally want OAT to seed from the bundle again; never delete seeded content you have edited, because the next install or update reseeds the bundled default in its place and your edits are gone. The finding is informational and carries no recovery command for exactly that reason.user-agent-unmaterialized— the pack installed canonical agents into~/.agents/agents/, but no active provider projects them at user scope. Every registered adapter (Claude, Cursor, Codex, Gemini, Copilot) syncs or natively reads user-scope agents, so while any of them is active for user sync the finding is suppressed. It appears when no provider is enabled for user sync, in which case every installed agent is listed. Provider access to canonical instructions through loaded, user, and project reads is a separate contract and does not clear this finding.oat tools updatecannot fix it. Enable a provider withoat providers set --scope user --enabled <provider>and runoat sync --scope user; reinstalling at project scope is not required. See Tool packs for how user-scope agents reach each provider.
status or doctor reports packs:inventory
OAT could not read the canonical managed-pack inventory. oat status keeps the
rest of the report available and returns packs.availability.status as
unavailable in JSON, with an empty packs.states array and a structured
diagnostic. oat doctor emits the same condition as a warning instead of
stopping its other checks.
The diagnostic message redacts only the roots that are part of the run: the
project root becomes a relative path (or .) when project scope is checked,
and the home root becomes ~ when user scope is checked. Redaction is a literal
replacement of those exact root paths, so a path outside them, such as a global
bundle path in an assets error, stays absolute in the message.
With an installed CLI, reinstall it so its bundled assets are complete and
match the CLI version, then rerun the command named in the diagnostic. If you
set OAT_ASSETS_DIR, point it at a complete asset bundle built for this CLI
version, or unset it to use the bundled copy.
npm install --global @open-agent-toolkit/cli
oat status # or: oat doctorThe diagnostic's own recovery line says to run pnpm build. That applies only
when you run the CLI from a checkout of the OAT source repository.
If you are working in the OAT source repository: rebuild the CLI workspace, then rerun the command named in the diagnostic:
pnpm build
oat status # or: oat doctorstatus or doctor reports legacy-false-conflict
An older repository left tools.<pack>: false in shared config while managed
assets for that pack are present. The false is never honored as an opt-out.
Choose one:
# adopt the existing install and rewrite intent as true
oat tools update --pack <pack> --scope <project|user>
# or remove the pack and delete the intent key entirely
oat tools remove --pack <pack> --scope <project|user>status or doctor reports duplicate-scope
The pack is installed at both project and user scope. This is a legitimate state, and OAT deliberately does not infer which copy a provider executes. Resolve it explicitly when you want one owner:
oat tools migrate --pack <pack> --from project --to user --dry-run
oat tools migrate --pack <pack> --from project --to userThe destination is installed and verified before source removal is offered, so declining the confirmation leaves you exactly where you started — installed at both scopes — rather than in a broken half-state.
oat tools migrate leaves the pack at both scopes
That is the safe outcome, not a failure. Migration verifies the destination
first and removes the source only after an explicit confirmation. Declining, or
running non-interactively, reports status retained-both. Rerun the command
interactively to finish the move; the destination work is already done.
If source removal or its sync failed partway, the command prints structured recovery instructions and retains source intent. Rerun the same command to complete it. Never delete the source by hand while the recovery instructions are outstanding.
A PJM command refuses to write and asks for oat pjm init
Installing the project-management pack installs the capability. It does
not adopt PJM for a repository, and oat backlog init / oat decision init are
not alternate adoption paths. oat pjm, oat backlog, and oat decision
mutations fail closed until adoption is recorded.
Inspect the state, then adopt:
oat pjm doctor --json # read adoption.state
oat pjm initadoption.state is declared (explicit marker present), inferred-legacy
(complete legacy scaffold, no marker), partial-initialization (incomplete
scaffold — rerun oat pjm init), or none. oat pjm init records
pjm.initialized in .oat/config.json after verifying the canonical scaffold.
A template change is not taking effect
Project lifecycle and PJM templates resolve repository → user → bundle, first
match wins (idea templates follow the ideas scope instead). A repository
template under .oat/templates/ is an owner override that pack updates never
rewrite, so it shadows the managed user default. Check which tier supplies a
template:
oat template resolve planDelete the repository copy to fall back to the managed default, or update the
managed default itself (workflows owns the project lifecycle templates,
project-management the PJM templates):
oat tools update --pack workflows --scope user
oat tools update --pack project-management --scope userA remote PJM command is pending, uncertain, or blocked
Read the JSON envelope before retrying:
oat pjm remote doctor --jsonBefore the first provider-contacting command, discover a matching live
connector. If no capable connector exists, inspect an already configured
provider CLI's live help. Construct only bounded provider-neutral capability
evidence and supply it through --capability-evidence-stdin.
pendingwithexternalActionmeans OAT durably prepared one exact semantic host action. Confirm the previously selected capability remains granted, execute that exact action at most once, then submit one bounded observation throughoat pjm remote operation continue.uncertainmeans an effect may have occurred. Follow the envelope's recovery instructions; never repeat a create or update blindly.needs-reviewrequires an explicit reconciliation choice.blockedmeans policy, approval, capability, lifecycle state, or outbound safety prevented the action. Do not bypass the gate or broaden the field projection.
Only an ok envelope after authoritative read-back is remote success. Native
provider output, a zero process exit, or a visible remote change is not.
A clone or worktree has unexpected remote PJM state
Linked worktrees share default local operational state through the Git common directory. If one worktree has a pending operation, inspect or continue it instead of starting a competing action elsewhere.
Independent clones do not share that local store. In a fresh clone, use
portable binding metadata plus oat pjm remote refresh through a matching live
host capability before treating snapshots as current. Offline, doctor and
migration remain available, but commands requiring remote freshness stay
pending or blocked.
If the repository opted into .oat/repo/pjm/remote/state/ or
<project>/remote/state/, remember that shared operational storage may contain
remote planning content visible to repository readers. Promotion requires an
exact persisted preview and fresh approval; local projects cannot use it.
--scope all fails outside a Git repository
It should not. oat status --scope all and oat doctor --scope all complete
the user-scope work and report project scope as unavailable.
oat tools update --scope all also completes the user-scope work, but it skips
project scope silently rather than naming it in the result, so an empty project
section there means "no repository here", not "nothing to update". An
explicitly requested --scope project outside a repository is still a hard
failure, which is intentional — you asked for a scope that does not exist here.
Manifest not found or invalid
- Missing manifest: run
syncorinit. For the repository's manifest (.oat/sync/manifest.json), useoat sync --scope projectoroat init --scope project; without--scope, both commands also write to your home directory. For the user manifest (~/.oat/sync/manifest.json), use--scope user. - Invalid manifest: repair/remove file and rerun
Status/output mismatches with lifecycle expectations
- Reconcile
state.md,plan.mdreview table, andimplementation.md. - Ensure phase/review status has been updated after reviews and fix cycles.
An Explainer Kit recap ends as built-needs-review
built-needs-review is a satisfied generation outcome. The explainer page and
browser-free checks are complete, but the browser rung was unavailable or
reported a visual issue that needs a human look. Lifecycle closeout can
continue, and a fresh package that passes the complete package guard can be
selected and archived.
Inspect:
qa/result.jsonfor the selected browser rung, browser availability, and visual findings.manifest.jsonfor warnings, outcome, and the exact package inventory.site/index.htmlat 320, 768, and 1440 pixels when visual inspection is available.- Adapter logs for a browser launch or capture failure.
If the page is acceptable, retain the outcome as-is; the manifest honestly records that visual review is still due. If it is not acceptable, fix the source or authoring issue and generate a new run. Do not mutate a recorded run package.
Reference artifacts
In your own repository, these project files hold the evidence most entries on this page refer to:
.oat/projects/<scope>/<project>/implementation.md.oat/projects/<scope>/<project>/reviews/.oat/projects/<scope>/<project>/explainers/<slug>/manifest.json.oat/projects/<scope>/<project>/explainers/<slug>/qa/result.json
If you are working on OAT itself, in a checkout of the OAT source repository,
the checks behind oat doctor, oat status and oat instructions live in
these source files:
packages/cli/src/commands/doctor/index.tspackages/cli/src/commands/status/index.tspackages/cli/src/commands/tools/shared/pack-inventory.tspackages/cli/src/commands/instructions/