Hmbown/CodeWhale · error · Error

Invalid version 1 pet bucket.

Error message

Invalid version 1 pet bucket.

What it means

validatePetBucket enforces the version-1 PetBucket schema: exact simTimeMs/durationMs alignment to PET_BIN_MS, a known channel Category, boolean waiting, bounded agentIds (strings ≤4096 chars, ≤250k), safe-integer errors, and 13-element onsets/activeMs arrays within bounds. Any violation throws this error, so corrupt or foreign rows never enter telemetry.

Solutions

  1. Regenerate the offending row so every field matches the v1 schema (bin-aligned simTimeMs/durationMs, 13-length onsets and activeMs)
  2. Check the writer's schema version — upgrade the reader if the data is intentionally v2
  3. Sanitize ids to ≤4096 chars and counts to within the documented bounds at ingestion
  4. Skip/quarantine the invalid line instead of feeding it to decodePetJSONL/readTail

Example fix

// before
buckets.push({ simTimeMs: Date.now() % PET_BIN_MS, durationMs: 1234, ... });
// after
buckets.push({ simTimeMs: sequence * PET_BIN_MS, durationMs: PET_BIN_MS, onsets: new Array(13).fill(0), activeMs: new Array(13).fill(0), ... });
Defensive patterns

Strategy: validation

Validate before calling

function isLikelyValidBucket(b: any): boolean {
  return b && b.simTimeMs === b.sequence * PET_BIN_MS && b.durationMs === PET_BIN_MS
    && typeof b.waiting === 'boolean'
    && Array.isArray(b.onsets) && b.onsets.length === 13
    && Array.isArray(b.activeMs) && b.activeMs.length === 13
    && Number.isSafeInteger(b.errors) && b.errors >= 0;
}

Type guard

function isPetBucket(v: unknown): v is PetBucket {
  const b = v as PetBucket;
  return !!b && typeof b.sequence === 'number' && typeof b.simTimeMs === 'number'
    && Array.isArray(b.onsets) && b.onsets.length === 13
    && Array.isArray(b.activeMs) && b.activeMs.length === 13
    && typeof b.waiting === 'boolean' && Array.isArray(b.agentIds);
}

Try / catch

try {
  validatePetBucket(row);
} catch (e) {
  if (e.message === 'Invalid version 1 pet bucket.') {
    quarantine.push({ line: rawLine, reason: e.message }); // skip bad row, keep going
  } else throw e;
}

Prevention

When it happens

Trigger: decodePetJSONL, readTail, the telemetry store constructor, or acceptTelemetry receiving a parsed JSON row that fails any of the field checks — wrong bin alignment, unknown channel string, onsets.length !== 13, negative errors, oversized id strings, etc.

Common situations: A telemetry writer upgraded to a version-2 schema while the reader still expects v1; manually generated or synthetic JSONL rows with wrong field shapes; truncated lines that parse as JSON but lack fields; clock skew producing simTimeMs !== sequence*PET_BIN_MS.

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/d08112608fde8b99. Report an issue: GitHub.

Appendix: source

Thrown at pet/src/core/pet-telemetry.ts:30

  durationMs: number;
  onsets: number[];
  activeMs: number[];
  errors: number;
  agentIds: string[];
  waiting: boolean;
}

export function validatePetBucket(value: unknown): asserts value is PetBucket {
  validatePetState(value);
  const b = value as PetBucket;
  if (!b || typeof b !== 'object' || b.version !== 1 || !Number.isSafeInteger(b.sequence) || b.sequence < 0 || b.sequence > PET_MAX_SECONDS * 2.5
    || b.simTimeMs !== b.sequence * PET_BIN_MS || b.durationMs !== PET_BIN_MS
    || !CATEGORIES.includes(b.channel as Category) || typeof b.waiting !== 'boolean'
    || !Array.isArray(b.agentIds) || b.agentIds.length > 250_000 || b.agentIds.some(id => typeof id !== 'string' || !id || id.length > 4096)
    || !Number.isSafeInteger(b.errors) || b.errors < 0 || b.errors > 250_000
    || !Array.isArray(b.onsets) || b.onsets.length !== 13 || b.onsets.some(n => !Number.isSafeInteger(n) || n < 0 || n > 250_000)
    || !Array.isArray(b.activeMs) || b.activeMs.length !== 13 || b.activeMs.some(n => !Number.isFinite(n) || n < 0 || n > 100_000_000))
    throw new Error('Invalid version 1 pet bucket.');
}

export function decodePetJSONL(text: string): PetBucket[] {
  if (text.length > 64 * 1024 * 1024) throw new Error('Pet tape exceeds 64 MiB.');
  const rows = text.split(/\r?\n/).filter(line => line.trim()).map(line => JSON.parse(line) as unknown);
  if (rows.length > 216_000) throw new Error('Pet tape exceeds 24 hours.');
  return rows.map((row, i) => { validatePetBucket(row); if (row.sequence !== i) throw new Error('Non-contiguous pet tape.'); return row; });
}

/** A live file must advance before its contents count as a new observation.
 * Existing bytes, duplicate samples and a restarted sequence establish a
 * baseline; they never replay an old onset or human request. Drivers supply a
 * bounded tail and reset this cursor after suspension or a new attachment. */
export class PetLiveTape {
  private sequence: number | undefined;
  reset(): void { this.sequence = undefined; }
  readTail(text: string): PetBucket | undefined {
    if (!text) { this.reset(); return; }

View on GitHub (pinned to 433685b202)