affaan-m/ECC · error
Invalid install-state
Error message
Invalid install-state${label ? ` (${label})` : ''}: ${formatValidationErrors(result.errors)} What it means
assertValidInstallState validates an install-state object against the hand-rolled ecc.install.v1 schema (mirroring schemas/install-state.schema.json) and throws listing all validation errors. It runs on create, read, and write, so neither reading a corrupt state file nor writing a malformed state object can succeed silently.
Solutions
- Read the formatted validation errors in the message — they name the failing field and reason
- Restore the missing/renamed field per schemas/install-state.schema.json (ecc.install.v1)
- If the state file is from an older version, delete it and re-run the installer to regenerate it
- Fix the code path if your own script built the state object and omitted required fields
Example fix
// before
{ "installedAt": "2026-01-01T00:00:00Z" }
// after — include the schema-required version field
{ "version": "ecc.install.v1", "installedAt": "2026-01-01T00:00:00Z" } Defensive patterns
Strategy: validation
Validate before calling
// Validate against the published schema before use
const Ajv = require('ajv');
const schema = require('./schemas/install-state.schema.json');
const ajv = new Ajv();
const validate = ajv.compile(schema);
if (!validate(state)) console.error(validate.errors); // fix before calling installer APIs Type guard
function isInstallState(s) {
return !!s && typeof s === 'object' && !Array.isArray(s)
&& s.version === 'ecc.install.v1'
&& typeof s.installedAt === 'string';
} Try / catch
try {
const state = readInstallState();
} catch (e) {
if (/^Invalid install-state/.test(e.message)) {
// recover: delete/regenerate the state file per ecc.install.v1 schema
} else throw e;
} Prevention
- Always construct state objects via createInstallState rather than by hand
- Keep the state file aligned with schemas/install-state.schema.json (ecc.install.v1)
- After plugin/schema upgrades, regenerate the state file instead of reusing an old one
When it happens
Trigger: createInstallState/readInstallState/writeInstallState receive a state object missing required fields (e.g. version, installedAt), with wrong types, or an unrecognized ecc.install.v1 shape — validateInstallState returns valid:false and the formatted errors are thrown.
Common situations: Hand-editing .claude/install-state.json and dropping or renaming a required field; a plugin/schema version upgrade left an old state file that no longer validates; third-party tooling rewrote the state file with a different structure.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- -32602
- A managed install-state path is required before preflight.
- artifact path must be a non-empty relative path
- assets must be a nonempty list
- built an invalid spec
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/39e60221833357a1.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install-state.js:277
function formatValidationErrors(errors = []) {
return errors
.map(error => `${error.instancePath || '/'} ${error.message}`)
.join('; ');
}
function validateInstallState(state) {
const validator = getValidator();
const valid = validator(state);
return {
valid,
errors: validator.errors || [],
};
}
function assertValidInstallState(state, label) {
const result = validateInstallState(state);
if (!result.valid) {
throw new Error(`Invalid install-state${label ? ` (${label})` : ''}: ${formatValidationErrors(result.errors)}`);
}
}
function createInstallState(options) {
const installedAt = options.installedAt || new Date().toISOString();
const state = {
schemaVersion: 'ecc.install.v1',
installedAt,
target: {
id: options.adapter.id,
target: options.adapter.target || undefined,
kind: options.adapter.kind || undefined,
root: options.targetRoot,
installStatePath: options.installStatePath,
},
request: {
profile: options.request.profile || null,
modules: Array.isArray(options.request.modules) ? [...options.request.modules] : [],View on GitHub (pinned to 8321021c54)