can1357/oh-my-pi · error
Managed skill is ${bytes} bytes; the limit is ${MAX_MANAGED_
Error message
Managed skill is ${bytes} bytes; the limit is ${MAX_MANAGED_SKILL_BYTES}. Trim the body or description. What it means
Managed SKILL.md files are capped at MAX_MANAGED_SKILL_BYTES (64,000) UTF-8 bytes, measured on the final file content (frontmatter + description + body). writeManagedSkill throws with the actual and maximum byte counts when the serialized content exceeds the cap.
Source
Thrown at packages/coding-agent/src/autolearn/managed-skills.ts:170
export async function writeManagedSkill(input: WriteManagedSkillInput): Promise<{ path: string }> {
const name = sanitizeSkillName(input.name);
const description = sanitizeManagedDescription(input.description);
const body = input.body.trim();
// Reject empty content: an all-whitespace/control description sanitizes to ""
// and the `requireDescription` discovery scan then silently drops the skill,
// so the tool would report success for a skill that never appears.
if (!description) {
throw new Error(`Managed skill "${name}" needs a non-empty description.`);
}
if (!body) {
throw new Error(`Managed skill "${name}" needs a non-empty body.`);
}
const content = `${toSkillFrontmatter(name, description)}\n${body}\n`;
// Cap the UTF-8 byte size of the FINAL file (body + description + frontmatter),
// not the UTF-16 code-unit length of the body alone.
const bytes = Buffer.byteLength(content, "utf8");
if (bytes > MAX_MANAGED_SKILL_BYTES) {
throw new Error(
`Managed skill is ${bytes} bytes; the limit is ${MAX_MANAGED_SKILL_BYTES}. Trim the body or description.`,
);
}
return serializeSkillMutation(name, async () => {
await assertManagedRootSafe();
const dir = path.join(getManagedSkillsDir(), name);
const file = path.join(dir, "SKILL.md");
// Reject a symlinked skill directory: an intermediate symlink would let the
// write escape the isolated managed root. lstat does not follow the final
// component, so a symlinked `dir` is caught here.
const dirStat = await fs.lstat(dir).catch(err => {
if (isEnoent(err)) return null;
throw err;
});
if (dirStat?.isSymbolicLink()) {
throw new Error(
`Managed skill "${name}" resolves through a symlink; refusing to write outside the managed directory.`,
);View on GitHub (pinned to 9690622007)
Solutions
- Trim the body — keep only the essential instructions/steps
- Shorten the description to one concise line
- Measure with Buffer.byteLength(content, 'utf8') before writing to confirm you are under 64,000 bytes
- If the content is genuinely large, summarize it or store the full content elsewhere and reference it
Example fix
// before
writeManagedSkill({ name: "foo", description: desc, body: hugeLogDump });
// after
const body = hugeLogDump.split("\n").slice(0, 200).join("\n"); // keep essentials
writeManagedSkill({ name: "foo", description: desc, body }); Defensive patterns
Strategy: validation
Validate before calling
const content = `${name}\n${description}\n${body}`;
if (Buffer.byteLength(content, "utf8") > 64_000) throw new Error("skill too large"); Try / catch
try {
await writeManagedSkill(input);
} catch (err) {
const m = /Managed skill is (\d+) bytes/.exec(String((err as Error).message));
if (m) {
// trim body/description and retry
} else throw err;
} Prevention
- Measure with Buffer.byteLength(content, 'utf8'), not .length, before writing
- Summarize large dumps instead of embedding logs or whole files in the body
- Keep descriptions to one concise line to leave budget for the body
- Watch byte inflation from multi-byte characters in non-ASCII content
When it happens
Trigger: writeManagedSkill called with a body (or description) whose combined UTF-8 size exceeds 64,000 bytes — note multi-byte characters count more than their string length suggests.
Common situations: Auto-learn dumps a large log or entire file into the body; content with many non-ASCII characters inflates byte size past the limit even when string length looks fine; accumulated 'enhancement' appends grow the file over sessions.
Related errors
- ${name} exceeds the ${SHARPSHOOTER_MAX_FILE_LINES}-line limi
- ${error.message}
- Image file too large: ${formatBytes(inputBytes)} exceeds ${f
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/87ac936f84a2efe1.
Report an issue: GitHub.