{"record":{"id":"d29878539098a0fd","repo":"can1357/oh-my-pi","slug":"memorymessage-but-the-managed-skill-could-not","errorCode":null,"errorMessage":"${memoryMessage}, but the managed skill could not be written: ${reason}","messagePattern":"(.+?), but the managed skill could not be written: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/learn.ts","lineNumber":127,"sourceCode":"\t\t\t\tsafeSkillName = undefined;\n\t\t\t}\n\t\t\tif (params.skill.action === \"create\" && safeSkillName && isNameClaimedByAuthoredSkill(safeSkillName)) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `${memoryMessage}. Did not create managed skill \"${params.skill.name}\": an authored skill of that name already exists, and managed skills cannot override authored ones. Choose a different name.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tisError: true,\n\t\t\t\t\tdetails: { skill: null, shadowed: true },\n\t\t\t\t};\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait writeManagedSkill(params.skill);\n\t\t\t} catch (err) {\n\t\t\t\tconst reason = err instanceof Error ? err.message : String(err);\n\t\t\t\tthrow new Error(`${memoryMessage}, but the managed skill could not be written: ${reason}`);\n\t\t\t}\n\t\t\tconst verb = params.skill.action === \"create\" ? \"Created\" : \"Updated\";\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `${memoryMessage}. ${verb} managed skill \"${params.skill.name}\".` }],\n\t\t\t\tdetails: { skill: params.skill.name },\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `${memoryMessage}.` }],\n\t\t\tdetails: { skill: null },\n\t\t};\n\t}\n}\n","sourceCodeStart":109,"sourceCodeEnd":142,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/learn.ts#L109-L142","documentation":"The learn tool writes the managed skill only after the lesson has been stored. If writeManagedSkill(params.skill) throws (invalid skill name, filesystem error, name conflict), the tool wraps the original message into this combined Error so the developer knows the memory half succeeded but the skill half did not. The original reason text (err.message) is appended after the colon.","triggerScenarios":"Calling learn with a skill payload while writeManagedSkill fails — sanitizeSkillName rejects the name, the managed-skills directory is unwritable, disk is full, or an update targets a skill that cannot be written.","commonSituations":"Skill name with illegal characters that cannot be sanitized; read-only agent directory or full disk; concurrent writes corrupting the skill file; invalid markdown body rejected by the writer.","solutions":["Read the reason after the colon in the message to identify the underlying write failure.","Retry with a simpler kebab-case skill name that passes sanitization.","Fix filesystem issues (permissions, disk space) on the managed-skills directory.","Re-run learn without the skill payload if only the lesson needs storing — the lesson was already persisted."],"exampleFix":"// before: invalid name blows up writeManagedSkill\n{ \"skill\": { \"action\": \"create\", \"name\": \"My Cool Skill!!\", \"description\": \"...\", \"body\": \"...\" } }\n// after: use a sanitizable kebab-case name\n{ \"skill\": { \"action\": \"create\", \"name\": \"my-cool-skill\", \"description\": \"...\", \"body\": \"...\" } }","handlingStrategy":"try-catch","validationCode":"const safeName = sanitizeSkillName(params.skill.name); // throws early on unusable names\nif (!params.skill.description || !params.skill.body) {\n  throw new Error(\"Managed skill needs description and body before calling learn.\");\n}","typeGuard":"function isWritableSkill(skill: LearnParams[\"skill\"]): boolean {\n  return !!skill && [\"create\", \"update\"].includes(skill.action) &&\n    !!sanitizeSkillNameSafe(skill.name) && !!skill.description && !!skill.body;\n}","tryCatchPattern":"try {\n  await learnTool.execute(id, params);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"managed skill could not be written\")) {\n    // memory was stored; retry only the skill half (manage_skill) after fixing the reason\n  } else {\n    throw err;\n  }\n}","preventionTips":["Use plain kebab-case skill names so sanitization is a no-op.","Ensure the managed-skills directory is writable and has free space.","Remember the lesson is already stored — only the skill half needs retrying."],"tags":["filesystem","skill-writing","partial-failure","memory"],"backgroundTag":"skill-write-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}