can1357/oh-my-pi · error · Error
"${params.action}" requires both "description" and "body".
Error message
"${params.action}" requires both "description" and "body". What it means
The manage_skill schema's cross-field narrow already rejects create/update calls missing description or body, so this Error in execute() is a defensive narrowing guard: it proves the strings are present for writeManagedSkill's typed contract. It fires only when empty strings or falsy values slip past schema validation (e.g. description: "" passes the undefined check but fails this guard).
Source
Thrown at packages/coding-agent/src/tools/manage-skill.ts:69
if (!session.settings.get("autolearn.enabled")) return null;
return new ManageSkillTool(session.refreshSkills);
}
async execute(_id: string, params: ManageSkillParams): Promise<AgentToolResult> {
if (params.action === "delete") {
await deleteManagedSkill(params.name);
await this.refreshSkills?.();
return {
content: [{ type: "text", text: `Deleted managed skill "${params.name}".` }],
details: { action: "delete", name: params.name },
};
}
// Defensive narrowing: the schema refine already rejects create/update
// without both fields, so this is unreachable for valid input — it only
// proves the strings are present to `writeManagedSkill`'s typed contract.
if (!params.description || !params.body) {
throw new Error(`"${params.action}" requires both "description" and "body".`);
}
// A managed skill resolves below any authored skill of the same name
// (authored always wins in discovery), so creating one under a name an
// authored skill already claims writes a file that never surfaces. Refuse
// up front rather than report a false "Created". `sanitizeSkillName`
// normalizes to the on-disk name the discovery scan compares against.
if (params.action === "create" && isNameClaimedByAuthoredSkill(sanitizeSkillName(params.name))) {
return {
content: [
{
type: "text",
text: `Cannot create managed skill "${params.name}": an authored skill of that name already exists, and managed skills cannot override authored ones. Choose a different name.`,
},
],
isError: true,
details: { action: "create", name: params.name, shadowed: true },
};
}View on GitHub (pinned to 9690622007)
Solutions
- Include both a non-empty description and a non-empty body in the manage_skill params.
- If calling execute() directly, validate params against the schema first.
- Use action "delete" if you do not intend to provide skill content.
Example fix
// before: empty body slips past schema but fails the guard
{ "action": "create", "name": "deploy", "description": "How to deploy", "body": "" }
// after: provide real content
{ "action": "create", "name": "deploy", "description": "How to deploy", "body": "## Steps\n1. Run bun test" } Defensive patterns
Strategy: validation
Validate before calling
if ((params.action === "create" || params.action === "update") &&
(!params.description || !params.body)) {
throw new Error("manage_skill create/update requires non-empty description and body.");
} Type guard
function isCompleteSkillParams(p: ManageSkillParams): p is ManageSkillParams & { description: string; body: string } {
return p.action === "delete" || (typeof p.description === "string" && p.description.length > 0 &&
typeof p.body === "string" && p.body.length > 0);
} Try / catch
try {
await manageSkillTool.execute(id, params);
} catch (err) {
if (err instanceof Error && err.message.includes('requires both "description" and "body"')) {
// re-issue with filled-in description and body
} else {
throw err;
}
} Prevention
- Always validate params against the schema (which enforces the cross-field narrow) before direct execute() calls.
- Never pass empty strings for description/body — the guard requires truthy values, not just defined ones.
- Use action "delete" when no content is intended.
When it happens
Trigger: Invoking manage_skill (or calling execute directly, bypassing schema validation) with action "create" or "update" where description or body is an empty string or otherwise falsy.
Common situations: Programmatic/SDK calls to execute() that skip the schema narrow; a model emitting description: "" which passes the undefined-only check but fails the truthiness guard.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- anthropic-messages: ${data.summary}
- Schema contains a circular object graph — cannot enforce str
- Schema node has no type, combinator, or $ref — cannot enforc
- Validation failed for tool "${toolCall.name}":\n${errors}\n\
- ClinePass model catalog response is missing clinePass
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8f1e6d7d8d79fa4f.
Report an issue: GitHub.