pbakaus/impeccable · error

manual_edit_buffer_invalid_schema

Error message

manual_edit_buffer_invalid_schema

What it means

readBufferStrict() (skill/scripts/live/manual-edits-buffer.mjs) reads the pending-manual-edits buffer at .impeccable/live/pending-manual-edits.json. The file parsed as valid JSON, but the root is not an object or parsed.entries is not an array — i.e. the shape { version: 1, entries: [...] } is violated. Strict readers (the apply/commit path) throw manual_edit_buffer_invalid_schema; lenient readers (readBuffer) silently reset to an empty buffer instead.

Solutions

  1. Inspect .impeccable/live/pending-manual-edits.json
  2. Rewrite it as { "version": 1, "entries": [ ... ] }, preserving the staged entries if they are recoverable
  3. Otherwise archive/delete the file to start a clean buffer — staged browser edits recorded in it are discarded and must be re-made

Example fix

// .impeccable/live/pending-manual-edits.json — before
[ { "id": "e1", "text": "..." } ]

// after
{ "version": 1, "entries": [ { "id": "e1", "text": "..." } ] }
Defensive patterns

Strategy: type-guard

Validate before calling

import fs from 'node:fs';

const p = '.impeccable/live/pending-manual-edits.json';
if (fs.existsSync(p)) {
  const parsed = JSON.parse(fs.readFileSync(p, 'utf-8'));
  if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.entries)) {
    throw new Error('buffer schema drifted — archive the file and re-stage edits');
  }
}

Type guard

const isValidEditBuffer = (v) =>
  !!v && typeof v === 'object' && !Array.isArray(v) && Array.isArray(v.entries);

Try / catch

try {
  buffer = readBufferStrict(cwd);
} catch (err) {
  if (err?.message === 'manual_edit_buffer_invalid_schema') {
    // Reset path: archive the drifted buffer, accept that staged edits are lost,
    // re-stage via the browser Save flow.
    fs.renameSync(bufferPath, bufferPath + '.invalid');
    buffer = { version: 1, entries: [] };
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: The buffer file was hand-edited or written in a different format: root is an array of entries, entries was renamed, or the whole staged-edit structure from an older/newer skill version is present.

Common situations: An agent editing the buffer directly instead of going through the browser Save path; skill upgrades that changed the buffer schema; hand-resolved merge conflicts on the file.

Related errors


AI-assisted analysis of pbakaus/impeccable@f88b2837a7 (2026-08-18). Data as JSON: /api/errors/76602c5f2a5e722d. Report an issue: GitHub.

Appendix: source

Thrown at skill/scripts/live/manual-edits-buffer.mjs:38

export function getBufferPath(cwd = process.cwd()) {
  return path.join(getLiveDir(cwd), BUFFER_FILENAME);
}

export function readBuffer(cwd = process.cwd()) {
  return readBufferInternal(cwd, { strict: false });
}

export function readBufferStrict(cwd = process.cwd()) {
  return readBufferInternal(cwd, { strict: true });
}

function readBufferInternal(cwd, { strict }) {
  const filePath = getBufferPath(cwd);
  try {
    const raw = fs.readFileSync(filePath, 'utf-8');
    const parsed = JSON.parse(raw);
    if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.entries)) {
      if (strict) throw new Error('manual_edit_buffer_invalid_schema');
      return { version: BUFFER_VERSION, entries: [] };
    }
    return { version: BUFFER_VERSION, entries: parsed.entries };
  } catch (err) {
    if (strict && err?.code !== 'ENOENT') {
      throw new Error('manual_edit_buffer_unreadable: ' + (err.message || String(err)));
    }
    return { version: BUFFER_VERSION, entries: [] };
  }
}

export function writeBuffer(cwd, buffer) {
  const filePath = getBufferPath(cwd);
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  fs.writeFileSync(filePath, JSON.stringify({ version: BUFFER_VERSION, entries: buffer.entries }, null, 2));
}

/**

View on GitHub (pinned to f88b2837a7)