Yeachan-Heo/oh-my-codex · error · Error

Refusing malformed phase in ${entryPath}.

Error message

Refusing malformed phase in ${entryPath}.

What it means

An active_skills entry may optionally carry a phase field, which must be a string when present. This error fires when phase exists but is not a string (number, boolean, object, null). phase typically labels a skill's lifecycle stage, so a non-string value signals schema corruption the library will not silently accept.

Source

Thrown at src/cli/index.ts:8344

  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);
  }
}

interface CancellationTestFaults {
  writeFailureMode?: string;
  rollbackFailureMode?: string;
}

async function cancelModes(
  args: string[] = [],
  options: { cwd?: string; testFaults?: CancellationTestFaults } = {},

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Change phase to a string (e.g. 2 -> "2") or remove the field
  2. If the value is meaningful, map it to the correct string phase label for your version
  3. Retry the cancel command
  4. Check release notes for phase schema changes after upgrading

Example fix

// before
{ "skill": "x", "phase": 2 }
// after
{ "skill": "x", "phase": "build" }
Defensive patterns

Strategy: validation

Validate before calling

function phasesValid(entries: Record<string, unknown>[]): boolean {
  return entries.every((e) => !Object.prototype.hasOwnProperty.call(e, "phase") || typeof e.phase === "string");
}

Type guard

function isOptionalString(v: unknown): v is string | undefined {
  return v === undefined || typeof v === "string";
}

Prevention

When it happens

Trigger: A state entry like {"skill": "x", "phase": 2} or {"skill": "x", "phase": null}; the presence check passes but typeof entry.phase !== "string" is true.

Common situations: Schema changes between versions that made phase numeric; hand edits; migration scripts copying incompatible values into phase.

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.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/8fa3bf0fd14f7f99. Report an issue: GitHub.