{"record":{"id":"87ac936f84a2efe1","repo":"can1357/oh-my-pi","slug":"managed-skill-is-bytes-bytes-the-limit-is-ma","errorCode":null,"errorMessage":"Managed skill is ${bytes} bytes; the limit is ${MAX_MANAGED_SKILL_BYTES}. Trim the body or description.","messagePattern":"Managed skill is (.+?) bytes; the limit is (.+?)\\. Trim the body or description\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/autolearn/managed-skills.ts","lineNumber":170,"sourceCode":"export async function writeManagedSkill(input: WriteManagedSkillInput): Promise<{ path: string }> {\n\tconst name = sanitizeSkillName(input.name);\n\tconst description = sanitizeManagedDescription(input.description);\n\tconst body = input.body.trim();\n\t// Reject empty content: an all-whitespace/control description sanitizes to \"\"\n\t// and the `requireDescription` discovery scan then silently drops the skill,\n\t// so the tool would report success for a skill that never appears.\n\tif (!description) {\n\t\tthrow new Error(`Managed skill \"${name}\" needs a non-empty description.`);\n\t}\n\tif (!body) {\n\t\tthrow new Error(`Managed skill \"${name}\" needs a non-empty body.`);\n\t}\n\tconst content = `${toSkillFrontmatter(name, description)}\\n${body}\\n`;\n\t// Cap the UTF-8 byte size of the FINAL file (body + description + frontmatter),\n\t// not the UTF-16 code-unit length of the body alone.\n\tconst bytes = Buffer.byteLength(content, \"utf8\");\n\tif (bytes > MAX_MANAGED_SKILL_BYTES) {\n\t\tthrow new Error(\n\t\t\t`Managed skill is ${bytes} bytes; the limit is ${MAX_MANAGED_SKILL_BYTES}. Trim the body or description.`,\n\t\t);\n\t}\n\treturn serializeSkillMutation(name, async () => {\n\t\tawait assertManagedRootSafe();\n\t\tconst dir = path.join(getManagedSkillsDir(), name);\n\t\tconst file = path.join(dir, \"SKILL.md\");\n\t\t// Reject a symlinked skill directory: an intermediate symlink would let the\n\t\t// write escape the isolated managed root. lstat does not follow the final\n\t\t// component, so a symlinked `dir` is caught here.\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(\n\t\t\t\t`Managed skill \"${name}\" resolves through a symlink; refusing to write outside the managed directory.`,\n\t\t\t);","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/autolearn/managed-skills.ts#L152-L188","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nwriteManagedSkill({ name: \"foo\", description: desc, body: hugeLogDump });\n// after\nconst body = hugeLogDump.split(\"\\n\").slice(0, 200).join(\"\\n\"); // keep essentials\nwriteManagedSkill({ name: \"foo\", description: desc, body });","handlingStrategy":"validation","validationCode":"const content = `${name}\\n${description}\\n${body}`;\nif (Buffer.byteLength(content, \"utf8\") > 64_000) throw new Error(\"skill too large\");","typeGuard":null,"tryCatchPattern":"try {\n  await writeManagedSkill(input);\n} catch (err) {\n  const m = /Managed skill is (\\d+) bytes/.exec(String((err as Error).message));\n  if (m) {\n    // trim body/description and retry\n  } else throw err;\n}","preventionTips":["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"],"tags":["validation","size-limit"],"backgroundTag":"payload-size-limit-exceeded","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}