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

sandbox.md frontmatter evaluator.keep_policy must be a strin

Error message

sandbox.md frontmatter evaluator.keep_policy must be a string when provided.

What it means

parseKeepPolicy throws when sandbox.md frontmatter defines evaluator.keep_policy but its value is not a string (e.g. a number, boolean, or nested map). The parser only accepts a string (or omission/empty, which means undefined).

Source

Thrown at src/autoresearch/contracts.ts:138

      const section = result[currentSection];
      if (!section || typeof section !== 'object' || Array.isArray(section)) {
        throw contractError(`Invalid sandbox.md frontmatter section: ${currentSection}`);
      }
      (section as Record<string, unknown>)[key] = value;
      continue;
    }

    result[key] = value;
    currentSection = null;
  }

  return result;
}

function parseKeepPolicy(raw: unknown): AutoresearchKeepPolicy | undefined {
  if (raw === undefined) return undefined;
  if (typeof raw !== 'string') {
    throw contractError('sandbox.md frontmatter evaluator.keep_policy must be a string when provided.');
  }
  const normalized = raw.trim().toLowerCase();
  if (!normalized) return undefined;
  if (normalized === 'pass_only') return 'pass_only';
  if (normalized === 'score_improvement') return 'score_improvement';
  throw contractError('sandbox.md frontmatter evaluator.keep_policy must be one of: score_improvement, pass_only.');
}

export function parseSandboxContract(content: string): ParsedSandboxContract {
  const { frontmatter, body } = extractFrontmatter(content);
  const parsedFrontmatter = parseSimpleYamlFrontmatter(frontmatter);
  const evaluatorRaw = parsedFrontmatter.evaluator;

  if (!evaluatorRaw || typeof evaluatorRaw !== 'object' || Array.isArray(evaluatorRaw)) {
    throw contractError(EVALUATOR_BLOCK_ERROR);
  }

  const evaluator = evaluatorRaw as { command?: unknown; format?: unknown; keep_policy?: unknown };

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Set keep_policy to one of the quoted or unquoted string values: score_improvement or pass_only, e.g. 'keep_policy: pass_only'.
  2. Remove the key entirely if you want the default policy.
  3. Ensure it is a single scalar line, not an indented block.

Example fix

# before
evaluator:
  keep_policy:
    policy: pass_only

# after
evaluator:
  keep_policy: pass_only
Defensive patterns

Strategy: type-guard

Validate before calling

import { parseSandboxContract } from './contracts'; // parse first; keep_policy type errors surface here

// or pre-check the raw YAML value yourself before parsing:
function keepPolicyIsString(raw: unknown): boolean {
  return raw === undefined || typeof raw === 'string';
}

Type guard

const isKeepPolicyShaped = (raw: unknown): boolean =>
  raw === undefined || (typeof raw === 'string' && (['', 'pass_only', 'score_improvement'].includes(raw.trim().toLowerCase())));

Try / catch

try {
  parseSandboxContract(content);
} catch (err) {
  if ((err as Error).message.includes('keep_policy must be a string')) {
    // replace structured keep_policy with a scalar 'pass_only'/'score_improvement' or remove it
  }
  throw err;
}

Prevention

When it happens

Trigger: Frontmatter containing 'keep_policy: 2', 'keep_policy: true', or an indented block under keep_policy; parseSandboxContract passes evaluator.keep_policy into parseKeepPolicy and the typeof check fails.

Common situations: Quoting mistakes that still parse to non-strings in this simple parser, YAML booleans/numbers written without quotes where a policy name was intended, or copy-pasting a structured policy object.

Related errors


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