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

  1. Read the reason after the colon in the message to identify the underlying write failure.
  2. Retry with a simpler kebab-case skill name that passes sanitization.
  3. Fix filesystem issues (permissions, disk space) on the managed-skills directory.
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d29878539098a0fd. Report an issue: GitHub.