Error codes
Every coded failure is a
SkillErrorwith a stable.code. This page lists the codes and what raises each. Match on.code, never on.message.
The envelope
Every coded failure is a SkillError, carrying a .code (one of the stable error codes), a human-readable .message, and a .fields record of structured extras. A consumer that catches one can read those directly, or fold them into a single JSON object of its own:
{ "error": "<code>", "message": "<human-readable description>", "...": "extra fields" }e.g. JSON.stringify({ error: err.code, message: err.message, ...err.fields }). Some codes add structured fields to .fields (a path, a name, a rel, or at for the failing frontmatter key). Match on the .code, never on message text.
SkillError is thrown, not returned — createAgentSkills (via resourcePath) and the discoverSkills registry raise it directly. Read err.code, err.message, and err.fields at your boundary — build a JSON envelope from them yourself if you want one — and use setWarnSink to capture the separate stream of non-fatal warnings (malformed skill skipped, in-root duplicate, dangling symlink, name/dir mismatch). Discovery itself never throws for a bad skill unless strict is on — it warns and skips.
Codes
| Code | Meaning | Raised by (examples) |
|---|---|---|
invalid_skill | Malformed frontmatter or body. | parseSkill; discovery (discoverSkills) — a missing/misaligned closing --- fence, invalid YAML, a schema failure, or frontmatter that parses to a non-object scalar. Warned-and-skipped when lenient; thrown when strict. |
duplicate_skill | Two skills declaring the same name within one root. | discoverSkills when strict is on. Lenient keeps the first by sorted directory order and warns. A cross-root override — a later root in roots declaring the same name — is normal precedence (last wins), never this error. |
not_found | Unknown skill name, or a missing resource file. | resourcePath (unknown skill name; the resource does not exist); fsError on ENOENT. Note loadSkill returns undefined for an unknown name — it does not throw. |
not_a_file | A path was a directory where a file was expected. | resourcePath when rel resolves to a directory; fsError on EISDIR/ENOTDIR. |
path_escape | A relative resource path resolves outside the skill directory. | resourcePath and resolveResourcePath — via ../ traversal or a symlink pointing outside the (realpath-canonicalized) skill dir. An absolute rel is invalid_input, not this. |
invalid_input | A bad argument to a public function. | resourcePath and resolveResourcePath — an empty or absolute rel. |
io_error | An underlying filesystem failure while reading a skill. | Discovery/read paths; fsError fallback for any other Node ErrnoException. |
Some codes attach structured fields alongside the message — a path, a name, a rel, paths (the two conflicting directories for duplicate_skill), or at (the failing frontmatter key for a schema failure). Read them off err.fields.
Handling them
- Strict vs lenient. With
strict: false(the default) a malformed skill or an in-root duplicate is a warning, not a throw: discovery drops the offending directory and keeps going, solistSkills()returns the healthy skills. Setstrict: trueto turninvalid_skillandduplicate_skillinto thrown errors instead — useful in CI. A cross-root override (a later root inrootsshadowing an earlier one of the samename) is never a duplicate and never throws, even understrict. resourcePathhas four failure modes. It throwsinvalid_inputwhenrelis empty or absolute,path_escapewhen a relative target escapes the skill directory,not_foundwhen the skill name is unknown or the resource file is missing, andnot_a_filewhenrelresolves to a directory. See Resource confinement for exactly how the boundary is enforced.invalid_inputfromresolveResourcePath. The lower-levelresolveResourcePath(skillDir, rel)validates its argument up front: an emptyrelor an absoluterelisinvalid_input. UnlikeresourcePath, it performs neither an existence nor an is-file check — so it never raisesnot_foundornot_a_file, onlyinvalid_inputorpath_escape.
See also
- API reference → Errors & warnings —
SkillError,fsError,setWarnSink - Resource confinement — how
path_escape/not_found/not_a_fileare enforced