Hmbown/CodeWhale · error · Error

Invalid : object required.

Error message

Invalid ${field}: object required.

What it means

object is the evidence-module validator requiring a field to be a plain object (not null, not an array). It throws 'Invalid <field>: object required.' when the value is missing, a primitive, an array, or null. Nested sections of observations, policies, and bundles all route through it.

Solutions

  1. Locate the named field and make it a non-null, non-array object: {} at minimum if empty is allowed.
  2. Replace null with an empty object or remove the key only where the field is genuinely optional per the schema.
  3. Fix producers that serialize maps as arrays (use keyed objects).
  4. Diff your payload against the current evidence schema to catch nesting-depth changes.

Example fix

// before
validatePolicy({ authority: null })
// after
validatePolicy({ authority: { permitted: [], denied: [] } })
Defensive patterns

Strategy: type-guard

Validate before calling

function checkObject(v, field) {
  if (!v || typeof v !== 'object' || Array.isArray(v)) throw new Error(`${field} must be a plain object`);
}

Type guard

const isPlainObject = (v) => !!v && typeof v === 'object' && !Array.isArray(v);

Try / catch

try {
  validatePolicy(policy);
} catch (e) {
  if (e.message.includes('object required')) {
    console.error(`${e.message} — section missing or wrong shape in payload`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validateObservation/validatePolicy or builders with a section set to null, an array, a string, or simply omitted; JSON payloads where an object was serialized as a list or flattened.

Common situations: A producer writing [] where an object map was expected; JSON null for absent optional sections; refactored payload shapes after a version upgrade; hand-built payloads that nested one level too shallow.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/8e7e0ed962d59cf2. Report an issue: GitHub.

Appendix: source

Thrown at pet/src/core/evidence.ts:64

  id: string; code: string; severity: 'info'|'warning'|'critical';
  title: string; detail: string; recordIds: string[]; time: number;
}
export interface Interaction {
  runId: string; sandboxId?: string; group?: string; target: string;
  kind: string; stage: Stage; effect: Effect; count: number; recordIds: string[];
}
export interface BoundaryAnalysis {
  version: 1; omittedFindings: number; asOf: number; inputRecords: number; visibleRecords: number; uniqueRecords: number;
  futureRecords: number; findings: BoundaryFinding[]; coverage: CoverageRow[];
  interactions: Interaction[]; runs: number; sandboxes: number; externalEffects: number;
  unknownEffects: number; authority: { permitted: number; denied: number; unknown: number };
  provenance: string;
}
const enumValue = <T extends string>(v:unknown, values: readonly T[], field:string): T => {
  if(typeof v!=='string'||!values.includes(v as T))throw new Error(`Invalid ${field}.`);return v as T;
};
const object = (v:unknown, field:string): Record<string,unknown> => {
  if(!v||typeof v!=='object'||Array.isArray(v))throw new Error(`Invalid ${field}: object required.`);return v as Record<string,unknown>;
};
const text = (v:unknown, field:string, max=256):string => {
  if(typeof v!=='string'||!v.length||v.length>max||/[\u0000-\u001f\u007f]/.test(v))throw new Error(`Invalid ${field}: nonempty bounded text required.`);return v;
};
const num = (v:unknown,field:string,min=0):number => {
  if(typeof v!=='number'||!Number.isFinite(v)||v<min||Math.abs(v)>Number.MAX_SAFE_INTEGER)throw new Error(`Invalid ${field}.`);return v;
};
const optionalText = (o:Record<string,unknown>,key:string):string|undefined => o[key]===undefined?undefined:text(o[key],key);
const strings = (v:unknown,field:string,allowEmpty=false):string[]=>{
  if(!Array.isArray(v)||(!allowEmpty&&!v.length)||v.length>256)throw new Error(`Invalid ${field}.`);
  const out=v.map(x=>text(x,field));if(new Set(out).size!==out.length)throw new Error(`Duplicate ${field}.`);return out;
};
const boolean = (v:unknown,field:string):boolean=>{if(typeof v!=='boolean')throw new Error(`Invalid ${field}.`);return v;};
const optionalNumber=(o:Record<string,unknown>,key:string)=>o[key]===undefined?undefined:num(o[key],key);
function onlyKeys(o:Record<string,unknown>,keys:string[],label:string):void{
  for(const k of Object.keys(o))if(!keys.includes(k))throw new Error(`Unknown ${label} field: ${k}.`);
}
/** All unrecognized fields are rejected, never secretly retained in metadata-only evidence. */

View on GitHub (pinned to 433685b202)