{"record":{"id":"58fed49463162949","repo":"can1357/oh-my-pi","slug":"managed-skill-safe-does-not-exist","errorCode":null,"errorMessage":"Managed skill \"${safe}\" does not exist.","messagePattern":"Managed skill \"(.+?)\" does not exist\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/autolearn/managed-skills.ts","lineNumber":250,"sourceCode":"/** Delete a managed skill directory. Throws when it does not exist. */\nexport async function deleteManagedSkill(name: string): Promise<void> {\n\tconst safe = sanitizeSkillName(name);\n\tawait serializeSkillMutation(safe, async () => {\n\t\tawait assertManagedRootSafe();\n\t\tconst dir = path.join(getManagedSkillsDir(), safe);\n\t\t// Refuse to follow a symlinked skill directory (rm would delete the target).\n\t\tconst dirStat = await fs.lstat(dir).catch(err => {\n\t\t\tif (isEnoent(err)) return null;\n\t\t\tthrow err;\n\t\t});\n\t\tif (dirStat?.isSymbolicLink()) {\n\t\t\tthrow new Error(`Managed skill \"${safe}\" is a symlink; refusing to delete outside the managed directory.`);\n\t\t}\n\t\ttry {\n\t\t\tawait fs.rm(dir, { recursive: true });\n\t\t} catch (err) {\n\t\t\tif (isEnoent(err)) {\n\t\t\t\tthrow new Error(`Managed skill \"${safe}\" does not exist.`);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t});\n}\n","sourceCodeStart":232,"sourceCodeEnd":256,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/autolearn/managed-skills.ts#L232-L256","documentation":"deleteManagedSkill maps an ENOENT from fs.rm (the skill directory vanished or never existed) into this explicit error rather than treating delete as a silent no-op. Deletion of a managed skill is expected to target an existing skill.","triggerScenarios":"deleteManagedSkill(name) when ~/.omp/agent/managed-skills/<name>/ does not exist — already deleted, never created, or name misspelled.","commonSituations":"Double-delete in a retry path; an agent cleaning up skills concurrently (same-name mutations are serialized in-process but not cross-process); typo in the skill name.","solutions":["Verify the skill exists before deleting (lstat the directory) and skip the delete if absent.","Check the skill name spelling against the managed-skills directory listing.","Treat this error as \"already gone\" and swallow it if your workflow is idempotent cleanup."],"exampleFix":"// before\nawait deleteManagedSkill(\"my-skill\");\n// after\nconst dir = `${getManagedSkillsDir()}/my-skill`;\nif (await Bun.file(dir).exists()) await deleteManagedSkill(\"my-skill\");","handlingStrategy":"validation","validationCode":"const dir = `${getManagedSkillsDir()}/${sanitizeSkillName(name)}`;\nconst exists = await fs.lstat(dir).then(() => true).catch(e => { if (isEnoent(e)) return false; throw e; });\nif (!exists) return; // already gone; idempotent delete","typeGuard":null,"tryCatchPattern":"try {\n  await deleteManagedSkill(name);\n} catch (err) {\n  if (!(err instanceof Error && err.message.includes('does not exist'))) throw err;\n  // treat as already deleted\n}","preventionTips":["Make delete paths idempotent: existence check first, or swallow the not-exist error.","Verify names against a fresh directory listing rather than stale in-memory lists.","Serialize cross-process deletes if multiple omp instances may prune concurrently."],"tags":["filesystem","missing-resource"],"backgroundTag":"file-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}