{"record":{"id":"dd24700ed492d7f2","repo":"can1357/oh-my-pi","slug":"file-not-found-targetpath","errorCode":null,"errorMessage":"File not found: ${targetPath}","messagePattern":"File not found: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/skill-protocol.ts","lineNumber":102,"sourceCode":"\t\t\t\tconst contained = await resolveContainedPath(skill.containRoot, resolvedPath);\n\t\t\t\tif (contained.status === \"outside\") {\n\t\t\t\t\tthrow new Error(`skill:// path resolves outside the plugin root: ${url.href}`);\n\t\t\t\t}\n\t\t\t\tif (contained.status === \"missing\") {\n\t\t\t\t\tthrow new Error(`File not found: ${resolvedPath}`);\n\t\t\t\t}\n\t\t\t\ttargetPath = contained.realPath;\n\t\t\t}\n\t\t} else {\n\t\t\ttargetPath = context?.pathOnly === true ? skill.baseDir : skill.filePath;\n\t\t}\n\n\t\tlet stats: fsTypes.Stats;\n\t\ttry {\n\t\t\tstats = await fs.stat(targetPath);\n\t\t} catch (error) {\n\t\t\tif (isEnoent(error)) {\n\t\t\t\tthrow new Error(`File not found: ${targetPath}`);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\tif (stats.isDirectory()) {\n\t\t\treturn buildDirectoryResource(url.href, targetPath);\n\t\t}\n\t\tif (!stats.isFile()) {\n\t\t\tthrow new Error(`skill:// URL must resolve to a file or directory: ${url.href}`);\n\t\t}\n\n\t\tconst content = await Bun.file(targetPath).text();\n\t\treturn {\n\t\t\turl: url.href,\n\t\t\tcontent,\n\t\t\tcontentType: getContentType(targetPath),\n\t\t\tsize: Buffer.byteLength(content, \"utf-8\"),\n\t\t\tsourcePath: targetPath,","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/skill-protocol.ts#L84-L120","documentation":"After computing the target path (either the skill's SKILL.md file, the skill's baseDir for path-only resolution, or a contained resolved path), the handler calls `fs.stat(targetPath)`. If stat fails with ENOENT the target does not exist, and the handler converts the raw ENOENT into this clearer 'File not found' error. Any other stat error (permissions, I/O) is rethrown unchanged.","triggerScenarios":"`SkillProtocolHandler.resolve()` where: (a) a `skill://<name>` URL is used and the skill's `filePath` (its SKILL.md) does not exist on disk; (b) a `skill://<name>/<path>` URL is used without a `containRoot` and `path.join(skill.baseDir, relativePath)` does not exist; (c) `context.pathOnly === true` and the skill's `baseDir` directory was deleted.","commonSituations":"Skill discovered from a config/marketplace but its SKILL.md was never written or was deleted; skills loaded from a stale cache pointing at removed directories; mistyped paths in skill:// URLs for non-plugin skills; a partially-synced skills directory.","solutions":["Stat the path from the error message directly on disk to confirm it is missing.","Re-install or re-sync the skill so its SKILL.md/baseDir exists (remove stale skill cache entries).","Fix the path in the skill:// URL — check spelling and the skill's actual directory layout.","If you control skill registration, ensure `filePath`/`baseDir` point at real paths at registration time."],"exampleFix":"// before\nconst res = await handler.resolve(parseInternalUrl(\"skill://old-skill\")); // SKILL.md deleted\n// after\nconst res = await handler.resolve(parseInternalUrl(\"skill://new-skill\")); // resynced skill","handlingStrategy":"try-catch","validationCode":"import * as fs from \"node:fs/promises\";\ntry {\n  await fs.stat(targetPath); // SKILL.md, baseDir, or joined path\n} catch {\n  throw new Error(`skill target missing on disk: ${targetPath}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const resource = await handler.resolve(url);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"File not found:\")) {\n    // resync skills, then retry once\n    await resyncSkills();\n    return handler.resolve(url);\n  }\n  throw err;\n}","preventionTips":["Keep the skills directory in sync; clear stale caches pointing at deleted paths.","Verify the skill's filePath/baseDir exists at registration time.","Validate the path portion of skill:// URLs against the skill directory listing.","Distinguish plugin skills (containRoot) from plain skills — their lookup paths differ."],"tags":["filesystem","skill-protocol","enoent","file-not-found"],"backgroundTag":"file-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}