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

  1. Regenerate the install plan so every merge-json operation carries a defined mergePayload.
  2. If building plans in code, set mergePayload (even to an empty object {}) instead of leaving it undefined.
  3. 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

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


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)