Hmbown/CodeWhale · error · Error

Invalid .

Error message

Invalid ${field}.

What it means

enumValue is the evidence-module validator for string fields restricted to a fixed set of allowed values. It throws when a value is not a string or is not one of the allowed literals, with the field name interpolated into the message (e.g. 'Invalid kind.'). It guarantees enum-typed fields in observations, policies, and bundles are exactly one of the documented values.

Solutions

  1. Check the error's field name and compare the value against the allowed literals for that field in the evidence schema.
  2. Fix casing/spelling to match the documented enum member exactly (comparison is case-sensitive).
  3. Ensure the field is present — undefined fails the string check too.
  4. If your producer predates a schema rename, regenerate or migrate the evidence payload to the current schema.

Example fix

// before
validateObservation({ kind: 'syscall' })          // not an allowed value
// after
validateObservation({ kind: 'sandbox_escape' })   // one of the schema's enum members
Defensive patterns

Strategy: validation

Validate before calling

const KINDS = ['sandbox_escape','syscall','net_connect','file_write']; // substitute the schema's literals
function checkEnum(v, field, values) {
  if (typeof v !== 'string' || !values.includes(v)) throw new Error(`${field} must be one of: ${values.join(', ')}`);
}

Type guard

const isOneOf = (v, values) => typeof v === 'string' && values.includes(v);

Try / catch

try {
  validateObservation(observation);
} catch (e) {
  if (e.message.startsWith('Invalid ') && !e.message.includes(':')) {
    console.error(`enum field rejected: ${e.message}; check spelling/casing against the schema`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validateObservation, validatePolicy, or the sources/g builders with a payload whose enum field (e.g. kind, effect class) is misspelled, has wrong casing, is a number/boolean, or is absent (undefined).

Common situations: Hand-writing evidence JSON with a typo like "permitted" vs "allowed"; a schema change between library versions renaming an enum member; serializing from another language where the enum became a number; forgetting to set the field at all.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

  count: number; lastReceivedAt: number | null; sequenceGaps: number;
}
export interface BoundaryFinding {
  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{

View on GitHub (pinned to 433685b202)