Yeachan-Heo/oh-my-codex · error · Error
Refusing malformed skill in ${entryPath}.
Error message
Refusing malformed skill in ${entryPath}. What it means
Each active_skills entry must carry a non-empty string skill identifier; this error fires when entry.skill is missing, not a string, or an empty/whitespace-only string. The skill name is the key cancellation logic uses to match and update entries, so it must be present and well-formed. The library refuses rather than guessing a default.
Source
Thrown at src/cli/index.ts:8336
function assertCompatibleNestedSkillOwners(
value: Record<string, unknown>,
authority: ExactSessionCancellationAuthority,
path: string,
): void {
if (!Object.prototype.hasOwnProperty.call(value, "active_skills")) return;
if (!Array.isArray(value.active_skills)) {
throw new Error(`Refusing malformed active_skills in ${path}.`);
}
for (const [index, skill] of value.active_skills.entries()) {
const entryPath = `${path} active_skills[${index}]`;
if (!skill || typeof skill !== "object" || Array.isArray(skill)) {
throw new Error(`Refusing malformed active_skills entry ${index} in ${path}.`);
}
const entry = skill as Record<string, unknown>;
if (typeof entry.skill !== "string" || entry.skill.trim() === "") {
throw new Error(`Refusing malformed skill in ${entryPath}.`);
}
for (const field of ["active"] as const) {
if (Object.prototype.hasOwnProperty.call(entry, field) && typeof entry[field] !== "boolean") {
throw new Error(`Refusing malformed ${field} in ${entryPath}.`);
}
}
if (Object.prototype.hasOwnProperty.call(entry, "phase") && typeof entry.phase !== "string") {
throw new Error(`Refusing malformed phase in ${entryPath}.`);
}
for (const [field, fieldValue] of Object.entries(entry)) {
if (field.endsWith("_at") && typeof fieldValue !== "string") {
throw new Error(`Refusing malformed ${field} in ${entryPath}.`);
}
}
assertExactSkillOwner(entry, authority, entryPath);
}
}
View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Locate the entry at the reported active_skills index in the state file
- Set skill to a non-empty trimmed string or remove the entry
- Save and retry the cancel command
- Validate state files with a schema check after external tooling writes them
Example fix
// before
{ "active": true }
// after
{ "skill": "deploy", "active": true } Defensive patterns
Strategy: validation
Validate before calling
function skillNamesValid(entries: Record<string, unknown>[]): boolean {
return entries.every((e) => typeof e.skill === "string" && e.skill.trim() !== "");
} Type guard
function isNamedSkillEntry(v: unknown): v is { skill: string } & Record<string, unknown> {
return isSkillEntry(v) && typeof (v as Record<string, unknown>).skill === "string" && (v as { skill: string }).skill.trim() !== "";
} Prevention
- Always include a non-empty skill name when adding entries
- Trim and assert skill names in any script that writes state
When it happens
Trigger: An entry like {}, {"skill": 42}, {"skill": ""}, or {"skill": " "} inside active_skills in the state file being cancelled against.
Common situations: Partial manual edits that added an entry without a skill name; schema drift between CLI versions; tools that emit empty placeholders for unused skill slots.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Refusing malformed active_skills in ${path}.
- Refusing malformed active_skills entry ${index} in ${path}.
- Refusing malformed ${field} in ${entryPath}.
- Refusing malformed phase in ${entryPath}.
- omx cancel --all is not supported; broad workspace cancellat
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/ec8a6c7323f0073a.
Report an issue: GitHub.