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
- Locate the named field and make it a non-null, non-array object: {} at minimum if empty is allowed.
- Replace null with an empty object or remove the key only where the field is genuinely optional per the schema.
- Fix producers that serialize maps as arrays (use keyed objects).
- 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
- Never serialize absent sections as null or []; omit the key or use {} per the schema.
- Keep producers and consumers on the same schema version to avoid nesting-shape drift.
- Add a pre-validate step that walks required sections and checks isPlainObject on each.
- Beware JSON round-trips that turn empty maps into arrays or null.
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
- Invalid .
- Codewhale terminal receipt contained a non-scalar field
- Duplicate .
- Duplicate policy identity.
- Facts must be scalar metadata, not content objects.
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)