affaan-m/ECC · warning
! : invalid meta.json, continuing with defaults ( )
Error message
! ${contextDir}: invalid meta.json, continuing with defaults (${e.message}) What it means
This is not a thrown error but a warning emitted by the CK v1→v2 context migration script in skills/ck/commands/migrate.mjs:118. When a context directory contains a meta.json file that fails JSON.parse (e.g. truncated, hand-edited, or corrupted file), the migration keeps going with an empty `meta` object ({}) so defaults are used for session IDs, dates, and summaries. The migration is deliberately non-fatal: losing v1 metadata is preferable to blocking the whole upgrade.
Solutions
- Validate the meta.json with `node -e "JSON.parse(require('fs').readFileSync('<path>','utf8'))"` to see the exact parse error and line.
- Fix the JSON syntax (or restore the file from git/backup), then re-run the migrate command.
- If the metadata is not needed, delete meta.json and let the migration proceed with defaults — the warning only means defaults will be substituted.
- Check for a BOM or encoding issue: `file meta.json` and re-save as plain UTF-8 without BOM.
Example fix
// before (broken meta.json)
{ "lastSessionId": 'abc123', }
// after
{ "lastSessionId": "abc123" } Defensive patterns
Strategy: fallback
Validate before calling
const fs = require('fs');
let meta = {};
const raw = fs.existsSync(metaPath) ? fs.readFileSync(metaPath, 'utf8') : '';
if (raw) { try { JSON.parse(raw); } catch (e) { console.warn(`meta.json invalid: ${e.message}`); } } Type guard
function isValidMeta(value) { return typeof value === 'object' && value !== null && !Array.isArray(value); } Try / catch
let meta = {};
try { meta = JSON.parse(readFileSync(metaPath, 'utf8')); }
catch (e) { console.warn(`invalid meta.json, using defaults: ${e.message}`); } Prevention
- Validate meta.json with a JSON linter before committing or migrating.
- Never hand-edit meta.json while a CK session may write to it concurrently.
- Back up context directories before running version migrations.
- Write meta.json atomically (temp file + rename) in tooling to avoid truncated files.
When it happens
Trigger: Running the CK migrate command against a context directory whose meta.json is syntactically invalid JSON — trailing commas, single quotes, BOM bytes, a partially written file, or comments. JSON.parse throws and the catch block prints the warning with the parse error message.
Common situations: Manual edits to meta.json that broke syntax; an editor or sync tool truncated the file mid-write; a script appended non-JSON lines; a crash during a previous CK session left a zero-byte or half-written meta.json.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- Cannot merge ECC configuration into invalid JSON at
- Failed to parse at
- Failed to parse
- Invalid JSON in at
- Invalid JSON in
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/ab744776b631fe41.
Report an issue: GitHub.
Appendix: source
Thrown at skills/ck/commands/migrate.mjs:118
if (existing.version === 2) {
console.log(` ✓ ${contextDir} — already v2, skipping`);
skipped++;
continue;
}
} catch { /* fall through to migrate */ }
}
console.log(`\n → Migrating: ${contextDir}`);
try {
// Read v1 files
const contextMd = existsSync(contextMdPath) ? readFileSync(contextMdPath, 'utf8') : '';
let meta = {};
if (existsSync(metaPath)) {
try {
meta = JSON.parse(readFileSync(metaPath, 'utf8'));
} catch (e) {
console.warn(` ! ${contextDir}: invalid meta.json, continuing with defaults (${e.message})`);
}
}
// Extract fields from CONTEXT.md
const description = extractSection(contextMd, 'What This Is') || extractSection(contextMd, 'About') || null;
const stackRaw = extractSection(contextMd, 'Tech Stack') || '';
const stack = stackRaw.split(/[,\n]/).map(s => s.replace(/^[-*]\s+/, '').trim()).filter(Boolean);
const goal = (extractSection(contextMd, 'Current Goal') || '').split('\n')[0].trim() || null;
const constraintRaw = extractSection(contextMd, 'Do Not Do') || '';
const constraints = parseBullets(constraintRaw);
const decisionsRaw = extractSection(contextMd, 'Decisions Made') || '';
const decisions = parseDecisionsTable(decisionsRaw);
const nextStepsRaw = extractSection(contextMd, 'Next Steps') || '';
const nextSteps = parseBullets(nextStepsRaw);
const blockersRaw = extractSection(contextMd, 'Blockers') || '';
const blockers = parseBullets(blockersRaw).filter(b => b.toLowerCase() !== 'none');
const leftOffRaw = extractSection(contextMd, 'Where I Left Off') || '';
const leftOffParsed = parseLeftOff(leftOffRaw);View on GitHub (pinned to 8321021c54)