pbakaus/impeccable · error · Error

Invalid IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES JSON

Error message

Invalid IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES JSON

What it means

Thrown by applyMockWrites() when IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES is set but does not parse into a plain object (it is JSON null, a primitive, or an array). The mock-writes var maps relative file paths to string contents that the mock runner writes under cwd; only an object map is meaningful.

Source

Thrown at skill/scripts/live-copy-edit-agent.mjs:517

    const parsed = parseCopyEditBatchResult(raw);
    if (parsed) return parsed;
    throw new Error('Invalid IMPECCABLE_LIVE_COPY_AGENT_MOCK_RESULT JSON');
  }
  return {
    status: 'done',
    appliedEntryIds: (batch?.entries || []).map((entry) => entry.id).filter(Boolean),
    failed: [],
    files: [],
    notes: ['mock copy-edit batch result'],
  };
}

function applyMockWrites(env, cwd) {
  const raw = env.IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES;
  if (!raw) return;
  const writes = tryParseJson(raw);
  if (!writes || typeof writes !== 'object' || Array.isArray(writes)) {
    throw new Error('Invalid IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES JSON');
  }
  for (const [relativeFile, content] of Object.entries(writes)) {
    if (typeof relativeFile !== 'string' || typeof content !== 'string') continue;
    const absolute = path.resolve(cwd, relativeFile);
    if (!isPathInsideOrEqual(cwd, absolute)) continue;
    fs.mkdirSync(path.dirname(absolute), { recursive: true });
    fs.writeFileSync(absolute, content, 'utf-8');
  }
}

export function parseCopyEditAgentResult(text) {
  const trimmed = String(text || '').trim();
  if (!trimmed) return null;

  const parsedOuter = tryParseJson(trimmed);
  if (parsedOuter) {
    if (typeof parsedOuter.result === 'string') {
      const nested = parseCopyEditAgentResult(parsedOuter.result);

View on GitHub (pinned to d14711ae3d)

Solutions

  1. Provide a JSON object mapping relative paths to string contents, e.g. {"src/copy.txt":"Hello"}.
  2. Validate with `echo "$IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES" | jq 'type'` — it must report "object".
  3. Unset the var if you do not need mock file writes.

Example fix

// before
export IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES='["src/copy.txt"]'
// after
export IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES='{"src/copy.txt":"Hello"}'
Defensive patterns

Strategy: try-catch

Validate before calling

function parseMockWrites(raw) {
  if (!raw) return {};
  let obj;
  try { obj = JSON.parse(raw); } catch { throw new Error('IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES is not valid JSON'); }
  if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
    throw new Error('mock writes must be a JSON object of path -> string');
  }
  return obj;
}

Type guard

function isMockWritesObject(raw) {
  if (!raw) return true;
  try {
    const o = JSON.parse(raw);
    return o && typeof o === 'object' && !Array.isArray(o);
  } catch { return false; }
}

Try / catch

try {
  const writes = JSON.parse(process.env.IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES);
  if (Array.isArray(writes)) throw new Error('expected object');
} catch (err) {
  console.error('Fix IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES:', err.message);
}

Prevention

When it happens

Trigger: Setting IMPECCABLE_LIVE_COPY_AGENT_MOCK_WRITES to a JSON array, a bare string, a number, or malformed JSON. Each entry's key and value must both be strings; non-string entries are skipped silently but a non-object top level throws.

Common situations: Building the writes map programmatically and serializing an array by mistake; quoting the JSON so it arrives as a stringified string rather than an object.

Related errors


AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13). Data as JSON: /api/errors/f67a5a9dd789ca7b. Report an issue: GitHub.