can1357/oh-my-pi · error · Error
${memoryMessage}, but the managed skill could not be written
Error message
${memoryMessage}, but the managed skill could not be written: ${reason} What it means
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.
Source
Thrown at packages/coding-agent/src/tools/learn.ts:127
safeSkillName = undefined;
}
if (params.skill.action === "create" && safeSkillName && isNameClaimedByAuthoredSkill(safeSkillName)) {
return {
content: [
{
type: "text",
text: `${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.`,
},
],
isError: true,
details: { skill: null, shadowed: true },
};
}
try {
await writeManagedSkill(params.skill);
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
throw new Error(`${memoryMessage}, but the managed skill could not be written: ${reason}`);
}
const verb = params.skill.action === "create" ? "Created" : "Updated";
return {
content: [{ type: "text", text: `${memoryMessage}. ${verb} managed skill "${params.skill.name}".` }],
details: { skill: params.skill.name },
};
}
return {
content: [{ type: "text", text: `${memoryMessage}.` }],
details: { skill: null },
};
}
}
View on GitHub (pinned to 9690622007)
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.
Example fix
// before: invalid name blows up writeManagedSkill
{ "skill": { "action": "create", "name": "My Cool Skill!!", "description": "...", "body": "..." } }
// after: use a sanitizable kebab-case name
{ "skill": { "action": "create", "name": "my-cool-skill", "description": "...", "body": "..." } } Defensive patterns
Strategy: try-catch
Validate before calling
const safeName = sanitizeSkillName(params.skill.name); // throws early on unusable names
if (!params.skill.description || !params.skill.body) {
throw new Error("Managed skill needs description and body before calling learn.");
} Type guard
function isWritableSkill(skill: LearnParams["skill"]): boolean {
return !!skill && ["create", "update"].includes(skill.action) &&
!!sanitizeSkillNameSafe(skill.name) && !!skill.description && !!skill.body;
} Try / catch
try {
await learnTool.execute(id, params);
} catch (err) {
if (err instanceof Error && err.message.includes("managed skill could not be written")) {
// memory was stored; retry only the skill half (manage_skill) after fixing the reason
} else {
throw err;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- unknown filetype: {ft_debug}
- Is a directory
- Too many levels of symbolic links
- {}: {error}
- failed to create a unique fc temporary file
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d29878539098a0fd.
Report an issue: GitHub.