Hmbown/CodeWhale · error · Error

Unknown field: .

Error message

Unknown ${label} field: ${k}.

What it means

`onlyKeys` enforces a closed schema: any key on the input object not in the allowed list throws `Unknown ${label} field: ${k}.`. This library rejects unrecognized fields outright (never stores them silently), so typo'd or extra keys fail fast. `label` tells you which object (observation, time, subject, source, grant, target, ...) rejected it.

Solutions

  1. Remove the unknown key from the input object
  2. Fix the key's spelling/casing to match the allowed list shown in the error
  3. Move free-form metadata into `facts` (observations) or drop it; check the library's current schema for the renamed field

Example fix

// before
{"id":"s1","stage":"plan","descripton":"oops"}
// after
{"id":"s1","stage":"plan","description":"fixed typo"}
Defensive patterns

Strategy: validation

Validate before calling

const allowed = ['version','id','sources','grants','expectedSurfaces','forbiddenCrossGroup']; for (const k of Object.keys(policy)) if (!allowed.includes(k)) console.warn(`Unknown policy field: ${k}`);

Type guard

const onlyKnownKeys = (o: object, keys: string[]) => Object.keys(o).every(k => keys.includes(k));

Try / catch

try { validateObservation(rec); } catch (e) { const m = e.match?.(/^Unknown (\w+) field: (\w+)/) ?? /^Unknown (\w+) field: (\w+)/.exec(e.message); if (m) console.warn(`Strip or rename '${m[2]}' on ${m[1]}`); throw e; }

Prevention

When it happens

Trigger: validateObservation/validatePolicy/validateBundle given an object containing an extra or misspelled key, e.g. `runid` instead of `runId`, or a leftover key like `notes` on a source/grant; nested objects (time, subject, target, authority, correlation, facts containers) are checked with their own key lists.

Common situations: Older bundle format field removed in v1; typo in a hand-edited policy; adding custom metadata keys that the schema does not allow (use `facts` for that in observations); a producer upgraded ahead of the schema.

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


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

Appendix: source

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

};
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. */
export function validateObservation(input:unknown):Observation {
  const o=object(input,'observation');
  onlyKeys(o,['version','id','runId','operationId','sourceId','epoch','sequence','time','receivedAt','subject','stage','surface','action','effect','status','target','actionDigest','authority','correlation','facts'],'observation');
  if(o.version!==1)throw new Error('Unsupported observation version.');
  const t=object(o.time,'time'),s=object(o.subject,'subject');
  onlyKeys(t,['wallMs','clockId','monotonicMs','taskMs','uncertaintyMs'],'time');onlyKeys(s,['agentId','sandboxId','isolationGroup'],'subject');
  const seq=num(o.sequence,'sequence',1);if(!Number.isSafeInteger(seq))throw new Error('sequence must be an integer.');
  const facts=object(o.facts??{},'facts'),safeFacts:Observation['facts']={};
  if(Object.keys(facts).length>48)throw new Error('Too many observation facts.');
  for(const [k,v] of Object.entries(facts)){
    text(k,'fact key',80);if(['__proto__','prototype','constructor'].includes(k))throw new Error('Unsafe fact key.');
    if(typeof v==='string')safeFacts[k]=text(v,'fact value',512);
    else if(typeof v==='number')safeFacts[k]=num(v,'fact value',-Number.MAX_SAFE_INTEGER);
    else if(v===null||typeof v==='boolean')safeFacts[k]=v;
    else throw new Error('Facts must be scalar metadata, not content objects.');
  }

View on GitHub (pinned to 433685b202)