Repository navigation
Description
Summary
When a model invokes the skill tool with the name of a registered agent
instead of a skill, the only feedback is Unable to load skill <name>. The
error does not distinguish "unknown skill" from "permission denied" from "this
name belongs to an agent". Because user @-mentions of agents look similar to
skill invocations, models conflate the two registries, and the opaque error
sends the conversation into a manual debugging loop instead of a one-step
self-correction.
Environment
- opencode version: v0.0.0-beta-18743 (opencode2, V2)
- OS: Linux 7.1.12-200.fc44.x86_64 (Fedora 44, x64)
- Terminal: ghostty (TERM=xterm-256color, COLORTERM=truecolor)
- Shell: /bin/bash
- Install/channel: beta
- Active plugins: none (no
pluginconfig, no.opencode/plugin/)
Reproduction
- Define a subagent at
~/.config/opencode/agents/my-agent.mdwith
mode: subagentin the frontmatter (any name works; no skill with the
same name is defined). - In a session, ask the model to do a task "using @my-agent".
- Observe the model call the
skilltool withname: "my-agent".
Expected Behavior
The skill tool error should be actionable. Ideally:
Unable to load skill my-agent: unknown skill. "my-agent" is a registered agent; invoke it with the subagent/task tool.when the name matches an
agent, or minimallyUnable to load skill my-agent: no skill with this name is available in this session— i.e. distinguish "not found" from "permission denied", and point
at similarly named registered entities (skills and agents).
Actual Behavior
All failure modes collapse into the same opaque message:
Unable to load skill my-agent
From packages/core/src/tool/skill.ts, unableToLoad(name) is used for both
the not-found path (skills.list() miss) and mapped load/permission errors,
so the model cannot tell why the load failed or what the correct tool would
have been. In practice this required a manual config investigation
(agents directory, skills list) to work out that the mentioned name was an
agent, not a skill.
Additional Context
- The user config was correct:
mode: subagentagent definitions in
~/.config/opencode/agents/are the intended mechanism, and the same
session successfully invoked a different agent via the proper tool. - The confusion seems easy to trigger because
@-mentionsof agents and
skill names appear in similar prompt positions, and both registries are
name-based. - Related open issue: [FEATURE]: stop using Effect.die for skill-not-found in the skill tool #40091 (skill-not-found error handling uses
Effect.die) touches the same code path, but is about crash-style failure
handling rather than error-message quality / agent cross-referencing. - Frequency: reproduced 3 times across one session (each failed skill load
recovered only via manual fallback). - Workaround: none within the session; the model fell back to manually
executing the agent's instructions (loading the referenced skill and
calling the repository CLI directly), which produced correct output but
bypassed the subagent isolation.
Activity
holny commentedon Sep 2, 2026
I'd like to take this. In
tool/plugin/skill.ts, every miss inskills.get()collapses into one opaque failure, so agent names hit the same error as unknown skills. Plan: when the id isn't a skill, resolve it against the agent registry — if it matches an agent, fail with a hint telling the model to prompt the subagent instead; keep unknown-skill and permission failures distinct. Small change plus a focused test.holny commentedon Sep 9, 2026
Heads-up: the PR for this issue (#46940) was accidentally closed earlier today - its head fork got deleted unintentionally, which force-closed it. Re-filed as #48159 with the same commits. Sorry for the noise.
opencodejr commentedon Oct 28, 2026
Thanks for the detailed write-up, @Julfried!
To restate the request: when the
skilltool is called with an ID that isn't an available skill, the error should say so. If the ID matches a registered agent, it should also point the model to the task/subagent tool, so the model can correct itself instead of getting the genericUnable to load skill <name>.In the current code (
packages/core/src/tool/plugin/skill.ts), the identical message for "not found" and "permission denied" looks intentional. The tests check that a skill denied by policy returns exactly the same text as an unknown one, probably so the tool doesn't reveal which hidden skills exist. So I'm treating this as an enhancement rather than a bug. An agent hint only in the not-found case would avoid that concern. Maintainers will consider it.Related:
Effect.dierather than the message text; it was closed as not planned.