affaan-m/ECC · error
Missing merge payload for
Error message
Missing merge payload for ${operation.destinationPath} What it means
For a 'merge-json' operation, applyInstallPlanLocked clones the operation's mergePayload; if the payload is undefined (cloneJsonValue returns undefined only for an undefined input), the plan is malformed and the apply aborts. A merge operation without a payload cannot be applied, so the library fails fast instead of writing a no-op.
Solutions
- Regenerate the install plan so every merge-json operation carries a defined mergePayload.
- If building plans in code, set mergePayload (even to an empty object {}) instead of leaving it undefined.
- Upgrade/downgrade so the plan producer and applyInstallPlanLocked are the same ECC version.
Example fix
// before
{ kind: 'merge-json', destinationPath: settingsPath }
// after
{ kind: 'merge-json', destinationPath: settingsPath, mergePayload: { hooks: {} } } Defensive patterns
Strategy: type-guard
Validate before calling
// validate a plan before applying
for (const op of plan.operations) {
if (op.kind === 'merge-json' && op.mergePayload === undefined) {
throw new Error(`merge-json op for ${op.destinationPath} lacks mergePayload`);
}
} Type guard
const hasPayload = (op) => op.kind !== 'merge-json' || (op.mergePayload !== undefined && typeof op.mergePayload === 'object');
Try / catch
try {
await applyInstallPlan(plan);
} catch (e) {
if (e.message.startsWith('Missing merge payload')) {
plan = regeneratePlan(); // rebuild with payloads
await applyInstallPlan(plan);
} else throw e;
} Prevention
- Always construct merge-json operations with an explicit mergePayload.
- Keep plan producer and ECC apply code on the same version.
- Avoid JSON round-trips that drop undefined payload fields.
When it happens
Trigger: An install plan contains a merge-json operation whose mergePayload field is missing or explicitly undefined when applyInstallPlan runs — e.g. a programmatically constructed plan omitted the payload, or an older/newer plan format dropped it.
Common situations: Custom tooling generating install plans by hand; version skew between a plan file produced by one ECC version and applied by another; serialization dropping undefined payload fields.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Failed to parse at
- Invalid at : expected a JSON object
- Another ECC process is updating Claude settings
- application request must be a JSON object
- At least one guided harness must be selected
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/d43a6e0ec37a0937.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/apply.js:536
}
return merged;
}, {
lockHeld: settingsLockHeld,
beforeCommit() {
assertSafeInstallOperation(appliedPlan, operation);
},
});
writtenDestinations.add(operation.destinationPath);
if (operation.kind === 'remove-claude-settings-hooks') {
completedHookRemovalCount += 1;
}
continue;
}
if (operation.kind === 'merge-json') {
const payload = cloneJsonValue(operation.mergePayload);
if (payload === undefined) {
throw new Error(`Missing merge payload for ${operation.destinationPath}`);
}
const filteredPayload = (
isMcpConfigPath(operation.destinationPath) && disabledServers.length > 0
)
? filterMcpConfig(payload, disabledServers).config
: payload;
const currentValue = readOptionalJsonObject(
operation.destinationPath,
'existing JSON config'
);
const mergedValue = deepMergeJson(currentValue, filteredPayload);
fs.writeFileSync(operation.destinationPath, formatJson(mergedValue), 'utf8');
writtenDestinations.add(operation.destinationPath);
continue;
}
View on GitHub (pinned to 8321021c54)