affaan-m/ECC · error

Invalid

Error message

Invalid ${label}: ${error.message}

What it means

parseJsonLikeValue accepts either an already-JSON value or a string it tries to JSON.parse. When a string payload (e.g. operation.mergePayload / payload / previousValue for update-claude-settings operations) is not valid JSON, the original JSON.parse SyntaxError message is wrapped as `Invalid <kind>.<key>: <message>`. The label identifies which operation field failed so the broken state record can be located.

Solutions

  1. Fix the string payload named by the error label (e.g. 'update-claude-settings.mergePayload') so it is valid JSON — validate with JSON.parse in isolation or a linter.
  2. If the value can be an object instead of a string, store it as a native JSON object/array rather than a serialized string.
  3. Regenerate the install state (re-run install) instead of hand-repairing the payload string.
  4. If the input may be JSON5 or comments, normalize it to strict JSON before handing it to the lifecycle functions.

Example fix

// before (state file)
"mergePayload": "{ 'hooks': { 'SessionStart': [] }, }"

// after
"mergePayload": "{\"hooks\":{\"SessionStart\":[]}}"
Defensive patterns

Strategy: validation

Validate before calling

function assertValidJsonString(value, label) {
  if (typeof value === 'string') JSON.parse(value); // throws early with a clear location
}

Type guard

function isJsonString(value) {
  if (typeof value !== 'string') return false;
  try { JSON.parse(value); return true; } catch { return false; }
}

Try / catch

try {
  await applyOperation(op);
} catch (err) {
  const m = /^Invalid ([\w.-]+): /.exec(err.message);
  if (m) {
    console.error(`Field ${m[1]} is not valid JSON; fix the state record and re-run.`);
  } else throw err;
}

Prevention

When it happens

Trigger: getOperationJsonPayload or getOperationPreviousJson reads a string-typed value from operation fields mergePayload/managedPayload/payload/value/expectedValue or previousValue/previousJson/originalValue, and JSON.parse fails on it — e.g. trailing commas, single quotes, comments, truncated JSON, or JS object-literal syntax stored as a string.

Common situations: Hand-editing install-state JSON payloads and leaving a trailing comma or unquoted key; a template or script serializing settings with JSON5-ish syntax; truncation of a long settings payload; paste of a JS object literal into a JSON string field.

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


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/953cb80de2c18c96. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/install-lifecycle.js:268

function cloneJsonValue(value) {
  if (value === undefined) {
    return undefined;
  }

  return JSON.parse(JSON.stringify(value));
}

function parseJsonLikeValue(value, label) {
  if (value === undefined) {
    return undefined;
  }

  if (typeof value === 'string') {
    try {
      return JSON.parse(value);
    } catch (error) {
      throw new Error(`Invalid ${label}: ${error.message}`);
    }
  }

  if (value === null || Array.isArray(value) || isPlainObject(value) || typeof value === 'number' || typeof value === 'boolean') {
    return cloneJsonValue(value);
  }

  throw new Error(`Invalid ${label}: expected JSON-compatible data`);
}

function getOperationTextContent(operation) {
  const candidateKeys = ['renderedContent', 'content', 'managedContent', 'expectedContent', 'templateOutput'];

  for (const key of candidateKeys) {
    if (typeof operation[key] === 'string') {
      return operation[key];
    }
  }

View on GitHub (pinned to 8321021c54)