pbakaus/impeccable · error · Error
manual_edit_buffer_invalid_schema
Error message
manual_edit_buffer_invalid_schema
What it means
Thrown by readBufferStrict() (strict mode) when the pending-manual-edits buffer file exists and parses as JSON but does not have the shape { entries: [] } — i.e. parsed is not an object or parsed.entries is not an array. The non-strict readBuffer() silently returns an empty buffer instead. stageEntry() calls readBufferStrict() before merging, so staging a Save hits this if the on-disk buffer is corrupted.
Source
Thrown at plugin/skills/impeccable/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 d14711ae3d)
Solutions
- Stop any live sessions writing to the buffer, delete .impeccable/live/pending-manual-edits.json, and re-stage edits from the browser.
- Repair the file to the canonical shape: { "version": 1, "entries": [] } (or with valid entry objects matching { id, pageUrl, element, ops, stagedAt }).
- Use the non-strict readBuffer() to recover the entries you can and writeBuffer() a clean replacement.
- If this recurs, investigate concurrent writers — the buffer is project-local and not lock-guarded.
Example fix
// before — .impeccable/live/pending-manual-edits.json
[{"id":"x","ops":[]}]
// after
{
"version": 1,
"entries": []
}
// or delete the file and re-stage from the browser Save action:
// rm .impeccable/live/pending-manual-edits.json Defensive patterns
Strategy: fallback
Validate before calling
import { readBuffer, writeBuffer, getBufferPath } from './live/manual-edits-buffer.mjs';
import fs from 'node:fs';
// Pre-check without throwing: use the non-strict read, then validate.
function safeReadBuffer(cwd) {
const buf = readBuffer(cwd); // never throws
if (!Array.isArray(buf?.entries)) {
// quarantine the corrupt file and reset
const p = getBufferPath(cwd);
if (fs.existsSync(p)) fs.renameSync(p, p + '.corrupt');
writeBuffer(cwd, { version: 1, entries: [] });
return { version: 1, entries: [] };
}
return buf;
} Type guard
/** True when the parsed buffer matches { entries: [] }. */
function isValidBufferShape(parsed) {
return parsed !== null
&& typeof parsed === 'object'
&& Array.isArray(parsed.entries);
} Try / catch
import { readBufferStrict } from './live/manual-edits-buffer.mjs';
let buf;
try {
buf = readBufferStrict(cwd);
} catch (err) {
if (/manual_edit_buffer_invalid_schema/.test(err.message)) {
// quarantine and reset rather than fail the stage
const p = getBufferPath(cwd);
if (fs.existsSync(p)) fs.renameSync(p, p + '.corrupt');
buf = { version: 1, entries: [] };
} else {
throw err;
}
} Prevention
- Use the non-strict readBuffer() for read-only UI paths; it returns an empty buffer on corruption instead of throwing.
- Avoid concurrent writers to pending-manual-edits.json — the buffer has no lock.
- If you hand-edit the file, keep the canonical { "version": 1, "entries": [...] } shape.
When it happens
Trigger: .impeccable/live/pending-manual-edits.json contains a JSON array, a primitive, or an object without 'entries' (e.g. {"ops":[]} after a hand-edit or a partial write). The file exists (ENOENT is handled separately) and JSON.parse succeeds, but the schema check fails. ENOENT does NOT throw even in strict mode — only a malformed-but-present file does.
Common situations: A previous write was interrupted mid-file (partial JSON that happens to parse); a tool/manual edit rewrote the buffer with a different schema; migration from an older buffer format where the top-level shape changed; concurrent writers corrupted the array. readBufferStrict is called from stageEntry and live-commit-manual-edits.
Related errors
- invalid catalog: ${validation.errors.join('; ')}
- config.json must be an object
- config.files (non-empty string array) required
- config.files must contain only non-empty strings
- config.exclude, if present, must be a string array
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/f9fc7af622791286.
Report an issue: GitHub.