Repository navigation
Description
Description
Summary
Project skills defined under .opencode/skills/<id>/ where <id> is a directory symlink into a central skill store stopped registering for one project. The empty skill-registration state for that location is persisted, survives service restarts, and is only invalidated by filesystem events on the skills source directory. Because symlink mtimes never change on their own, the state never refreshes. touch -h .opencode/skills/* triggers an immediate re-scan and all skills register.
Environment
- opencode version: 2.0.32
- OS: Linux 6.12.113+deb13-amd64
- Terminal: TERM_PROGRAM=herdr, TERM=xterm-256color, COLORTERM=truecolor
- Shell: /bin/bash
- Install/channel: latest
- Active plugins: global
opencode-nowledge-mem; project-local.opencode/plugins/{session-start,inject-subagent-context,inject-workflow-state}.js(verified unrelated: removing them did not fix the affected project, and adding them to a fresh sandbox did not break it)
Reproduction
Setup: git project where .opencode/skills/<id> entries are directory symlinks to ~/.skills/<id>, each target containing a SKILL.md with name + description frontmatter. The symlinks are committed to git (mode 120000). A sibling project with an identical setup works correctly.
- At some point during a window of repeated snapshot-update failures (see Additional Context), skills stopped registering for the affected project.
- Verify the stuck state:
opencode api get '/api/skill?location[directory]=<abs project path>'returns only the built-in skills (opencode,report); the model's available-skill list shows only builtins; loading by exact ID via theskilltool fails withUnable to load skill <id>. opencode service restart→ same result: only builtins. The empty registration persists across restarts.touch -h .opencode/skills/*(bump the symlink mtimes themselves) → within seconds all 14 project skills register, and a session that was already running receives the updated available-skill list on its next model step.
The same symlink arrangement registers fine in other projects and in fresh sandboxes (git and non-git, tracked and untracked), so symlink discovery itself works; the problem is the stuck per-location state.
Expected Behavior
- Skill discovery for a location should be re-computed on service start / location load, or at least recover from a transient failure instead of persisting an empty result indefinitely.
- Watcher coverage should include the resolved symlink targets, so content edits in the central skill store re-trigger discovery for every project referencing them.
Actual Behavior
- An empty skill registration is persisted and survives
opencode service restart. - No re-scan happens until a filesystem event fires directly on the source-dir entries (
touch -h), which directory symlinks never generate on their own.
Additional Context
Suspected original trigger — git snapshot updates fail on pathspecs that traverse a directory symlink:
$ git add -n -- ':(top,literal).opencode/skills/<id>/SKILL.md'
fatal: pathspec ':(top,literal).opencode/skills/<id>/SKILL.md' is beyond a symbolic link
(exit 128)
The server log contains 7391 occurrences of (locale-translated to English):
level=WARN message="failed to add snapshot files" exitCode=128
stderr="fatal: pathspec ':(top,literal).opencode/skills/<id>/SKILL.md' is beyond a symbolic link"
Observed snapshot divergence between the two projects (~/.local/share/opencode/snapshot/<repo-hash>/<tree>/index):
- healthy project: dereferenced real files under
.opencode/skills/**(116 entries, mode 100644, includingSKILL.md,references/,scripts/) - affected project (rebuilt fresh on 2.0.32 during debugging): only bare symlink entries (mode 120000), no
SKILL.mdpaths at all
FS-level discovery clearly reads through symlinked skill dirs in other projects, so the snapshot content may not be the discovery source — but it may explain how the original scan failed and left the empty state behind.
Diagnosis notes: GET /api/skill?location[directory]=<path> was used for all registration checks; results can lag a couple of seconds after filesystem changes, so sample repeatedly before concluding.
Workaround: touch -h <project>/.opencode/skills/*
Plugins
No response
OpenCode version
2.0.32
Steps to reproduce
No response
Screenshot
No response
Operating System
Debian13
Terminal
Ghostty
Activity
opencode-agent commentedon Oct 19, 2026
Thanks for the detailed report. I tried to reproduce this on 2.0.32 on Linux and couldn't get skills stuck:
.opencode/skills/<id>pointing into a central store registered all of its skills throughGET /api/skill?location[directory]=…. The first request to a location that hasn't loaded yet can return an empty list for a second or two, then the skills appear.opencode service restartthe skills registered again within a few seconds.As far as I can tell from the code, each location rescans
.opencode/skillsfrom scratch when it loads, so I couldn't find where an empty result would be kept across a restart. These would help narrow it down if it happens again:touch -h, the output ofopencode debug config(or the resolved config directories) for the affected project. That would show whether its.opencodedirectory was being picked up at all.skill(e.g.skills rescanned,skill source loaded,failed to load skill source) right afteropencode service restart, ideally with--log-level debug.opencode service statusreports a new process and version after the restart. If another service or an older version was still answering, that would explain why the restart changed nothing.opencode-agent commentedon Oct 22, 2026
We haven't heard back, so I'm closing this for now. On 2.0.32, skills in symlinked
.opencode/skills/<id>directories registered every time I tried, including afteropencode service restart, and I couldn't find anything in the code that keeps an empty skill list across restarts. Without the details asked for above, there isn't enough to go on.If it happens again, please reopen or comment here. Before running the
touch -hworkaround, please include:opencode debug configfor the affected projectskillfrom just after a restart, with--log-level debugopencode service statusshows after the restartThanks for the thorough write-up.