affaan-m/ECC · error

Invalid ${entityName}${label ? ` (${label})` : ''}: ${format

Error message

Invalid ${entityName}${label ? ` (${label})` : ''}: ${formatValidationErrors(result.errors)}

What it means

Thrown by assertValidEntity() when the AJV validator for the named entity returns invalid for the supplied payload. The message is composed of the entity name, an optional label (used to identify which row/caller failed), and a formatted list of AJV errors (instancePath + message for each). This is the runtime data-quality gate for anything written into the state store.

Source

Thrown at scripts/lib/state-store/schema.js:84

function formatValidationErrors(errors = []) {
  return errors
    .map(error => `${error.instancePath || '/'} ${error.message}`)
    .join('; ');
}

function validateEntity(entityName, payload) {
  const validator = getEntityValidator(entityName);
  const valid = validator(payload);
  return {
    valid,
    errors: validator.errors || [],
  };
}

function assertValidEntity(entityName, payload, label) {
  const result = validateEntity(entityName, payload);
  if (!result.valid) {
    throw new Error(`Invalid ${entityName}${label ? ` (${label})` : ''}: ${formatValidationErrors(result.errors)}`);
  }
}

module.exports = {
  assertValidEntity,
  formatValidationErrors,
  readSchema,
  validateEntity,
};

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Read the AJV errors in the message — each entry shows instancePath (the failing JSON pointer) and the constraint that failed.
  2. Diff your payload against the matching $defs.<entityName> block in schemas/state-store.schema.json.
  3. Use validateEntity(entityName, payload) (non-throwing) to get { valid, errors } and surface them to the caller instead of crashing.
  4. Regenerate fixtures from a known-good row after schema migrations.

Example fix

// before
assertValidEntity('session', row, 'import');
// -> Invalid session (import): /harness must be equal to one of the allowed values

// after
const { validateEntity } = require('scripts/lib/state-store/schema');
const result = validateEntity('session', row);
if (!result.valid) {
  log.warn('skipping invalid session', result.errors);
  continue;
}
assertValidEntity('session', row, 'import');
Defensive patterns

Strategy: validation

Validate before calling

const { validateEntity } = require('scripts/lib/state-store/schema');

function writeIfValid(entityName, payload, label) {
  const { valid, errors } = validateEntity(entityName, payload);
  if (!valid) {
    log.warn({ entityName, label, errors }, 'skipping invalid entity');
    return null;
  }
  return payload;
}

const clean = writeIfValid('session', row, 'import');
if (clean) persistSession(clean);

Type guard

const { validateEntity } = require('scripts/lib/state-store/schema');

function isValidEntity(entityName, payload) {
  return validateEntity(entityName, payload).valid;
}

Try / catch

try {
  assertValidEntity(entityName, payload, label);
} catch (error) {
  if (new RegExp(`^Invalid ${entityName}`).test(error.message)) {
    // route the row to a dead-letter queue for inspection
    deadLetterQueue.push({ entityName, payload, error: error.message });
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: Inserting a session row whose required fields (e.g. id, harness, state, started_at) are missing or wrongly typed; writing a workItem with a status outside the enum; passing a timestamp that is not an ISO string; payload shape drift after a schema migration that the caller has not caught up to.

Common situations: A new required field was added to the schema and old producers still omit it; an upstream adapter normalizes to the wrong casing (snake_case vs camelCase); a nullable column is sent as the string 'null'; test fixtures have drifted from the real schema.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/71db4f9d4c6a497f. Report an issue: GitHub.