{"record":{"id":"c596433e58c6e41e","repo":"Hmbown/CodeWhale","slug":"invalid-field-nonempty-bounded-text-required","errorCode":null,"errorMessage":"Invalid ${field}: nonempty bounded text required.","messagePattern":"Invalid (.+?): nonempty bounded text required\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/evidence.ts","lineNumber":67,"sourceCode":"export interface Interaction {\n  runId: string; sandboxId?: string; group?: string; target: string;\n  kind: string; stage: Stage; effect: Effect; count: number; recordIds: string[];\n}\nexport interface BoundaryAnalysis {\n  version: 1; omittedFindings: number; asOf: number; inputRecords: number; visibleRecords: number; uniqueRecords: number;\n  futureRecords: number; findings: BoundaryFinding[]; coverage: CoverageRow[];\n  interactions: Interaction[]; runs: number; sandboxes: number; externalEffects: number;\n  unknownEffects: number; authority: { permitted: number; denied: number; unknown: number };\n  provenance: string;\n}\nconst enumValue = <T extends string>(v:unknown, values: readonly T[], field:string): T => {\n  if(typeof v!=='string'||!values.includes(v as T))throw new Error(`Invalid ${field}.`);return v as T;\n};\nconst object = (v:unknown, field:string): Record<string,unknown> => {\n  if(!v||typeof v!=='object'||Array.isArray(v))throw new Error(`Invalid ${field}: object required.`);return v as Record<string,unknown>;\n};\nconst text = (v:unknown, field:string, max=256):string => {\n  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;\n};\nconst num = (v:unknown,field:string,min=0):number => {\n  if(typeof v!=='number'||!Number.isFinite(v)||v<min||Math.abs(v)>Number.MAX_SAFE_INTEGER)throw new Error(`Invalid ${field}.`);return v;\n};\nconst optionalText = (o:Record<string,unknown>,key:string):string|undefined => o[key]===undefined?undefined:text(o[key],key);\nconst strings = (v:unknown,field:string,allowEmpty=false):string[]=>{\n  if(!Array.isArray(v)||(!allowEmpty&&!v.length)||v.length>256)throw new Error(`Invalid ${field}.`);\n  const out=v.map(x=>text(x,field));if(new Set(out).size!==out.length)throw new Error(`Duplicate ${field}.`);return out;\n};\nconst boolean = (v:unknown,field:string):boolean=>{if(typeof v!=='boolean')throw new Error(`Invalid ${field}.`);return v;};\nconst optionalNumber=(o:Record<string,unknown>,key:string)=>o[key]===undefined?undefined:num(o[key],key);\nfunction onlyKeys(o:Record<string,unknown>,keys:string[],label:string):void{\n  for(const k of Object.keys(o))if(!keys.includes(k))throw new Error(`Unknown ${label} field: ${k}.`);\n}\n/** All unrecognized fields are rejected, never secretly retained in metadata-only evidence. */\nexport function validateObservation(input:unknown):Observation {\n  const o=object(input,'observation');\n  onlyKeys(o,['version','id','runId','operationId','sourceId','epoch','sequence','time','receivedAt','subject','stage','surface','action','effect','status','target','actionDigest','authority','correlation','facts'],'observation');","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/evidence.ts#L49-L85","documentation":"text is the evidence-module validator for string fields: the value must be a non-empty string of at most `max` characters (default 256) containing no control characters (U+0000–U+001F, U+007F). It throws 'Invalid <field>: nonempty bounded text required.' otherwise, protecting identifiers and labels embedded in evidence from being empty, oversized, or containing injection-prone control bytes.","triggerScenarios":"Calling any validator/builder that reads a text field with an empty string, a string longer than the limit, or one containing \\n, \\t, ANSI escapes, NUL bytes, or other control characters; passing undefined for a required text field.","commonSituations":"Multi-line descriptions pasted into single-line label fields; log lines or user input with embedded control characters or ANSI color codes; unbounded identifiers generated upstream; empty values after a trim/strip step removed whitespace.","solutions":["Trim and ensure the field is non-empty before validating.","Strip control characters (e.g. v.replace(/[\\u0000-\\u001f\\u007f]/g, '')) and truncate to the field's max length.","For genuinely long content, move it to a field with a larger bound or store it out-of-band with a short reference here.","Check for ANSI escape/color codes in terminal-derived strings and strip them at the source."],"exampleFix":"// before\nvalidateObservation({ label: process.stderrChunk }) // may contain ANSI/\\n\n// after\nconst label = process.stderrChunk.replace(/[\\u0000-\\u001f\\u007f]/g, '').slice(0, 256);\nvalidateObservation({ label });","handlingStrategy":"validation","validationCode":"const cleanText = (v, max = 256) =>\n  typeof v === 'string' ? v.replace(/[\\u0000-\\u001f\\u007f]/g, '').slice(0, max) : '';\nif (!cleanText(label)) throw new Error('label is empty after cleaning');","typeGuard":"const isBoundedText = (v, max = 256) => typeof v === 'string' && v.length > 0 && v.length <= max && !/[\\u0000-\\u001f\\u007f]/.test(v);","tryCatchPattern":"try {\n  validateObservation(observation);\n} catch (e) {\n  if (e.message.includes('nonempty bounded text required')) {\n    console.error(`${e.message} — strip control chars / enforce max length at the producer`);\n  } else throw e;\n}","preventionTips":["Sanitize any string derived from logs, terminals, or user input before storing it in evidence.","Enforce a max-length convention (<= 256) for identifier-like fields at write time.","Strip ANSI escapes and newlines when converting multi-line output to single-line labels.","After trimming, check non-emptiness — whitespace-only strings fail validation."],"tags":["validation","evidence","text","sanitization"],"backgroundTag":"invalid-argument-format","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}