Hmbown/CodeWhale · error · Error

Unknown category for

Error message

Unknown category for ${e.id}.

What it means

buildPyramid maps each event onto a channel via CATEGORIES.indexOf(e.category); an unknown category yields -1 and triggers this error, naming the event id. Categories must come from the library's fixed CATEGORIES list so channel arrays can be preallocated.

Solutions

  1. Set each event's category to one of the values in the exported CATEGORIES array.
  2. Check offline first: events.filter(e => !CATEGORIES.includes(e.category)) and fix those ids.
  3. Re-import the trace through importTrace so categories are normalized to the current schema.

Example fix

// before
{ id: 'a', category: 'llm_call', ... }
// after
{ id: 'a', category: 'inference', ... } // a value from CATEGORIES
Defensive patterns

Strategy: validation

Validate before calling

import { CATEGORIES } from 'pet-native/model.js';
const unknown = events.filter(e => !CATEGORIES.includes(e.category));
if (unknown.length) console.warn('unknown categories:', [...new Set(unknown.map(e => e.category))]);

Try / catch

try { buildPyramid(events) } catch (err) { const m = err.message.match(/Unknown category for (.+)\./); if (m) remapCategory(m[1]); throw err; }

Prevention

When it happens

Trigger: Passing events whose category is missing, renamed, or outside the current CATEGORIES enum — e.g. events from an older schema version or a custom category string.

Common situations: Schema drift after a library upgrade renamed/added categories, hand-written events with ad-hoc category labels, or events from another tool's taxonomy.

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

Appendix: source

Thrown at pet/ios/Resources/pet-native.js:1329

function buildPyramid(events, requestedDuration, maxBins = 16_384) {
    if (!Number.isInteger(maxBins) || maxBins < 16 || maxBins > 1_048_576)
        throw new Error('maxBins must be an integer in [16, 1048576].');
    let duration = requestedDuration ?? 1;
    for (const e of events)
        duration = Math.max(duration, e.endTime, e.startTime, e.status === 'error' ? (0, model_js_1.errorOnsetOf)(e) : 0);
    if (!Number.isFinite(duration) || duration < 0)
        throw new Error('Signal duration must be finite and nonnegative.');
    duration = Math.max(1, duration);
    const binMs = 2 ** Math.ceil(Math.log2(Math.max(1, duration / (maxBins - 1))));
    const length = Math.floor(duration / binMs) + 1, fine = emptyLevel(length, binMs);
    const stride = length + 1;
    const activeDiff = new Float64Array(model_js_1.CATEGORIES.length * stride), tokenDiff = new Float64Array(model_js_1.CATEGORIES.length * stride);
    for (const e of events) {
        if (!Number.isFinite(e.startTime) || !Number.isFinite(e.endTime) || e.startTime < 0 || e.endTime < e.startTime)
            throw new Error(`Invalid interval for ${e.id}.`);
        const channel = model_js_1.CATEGORIES.indexOf(e.category);
        if (channel < 0)
            throw new Error(`Unknown category for ${e.id}.`);
        const a = Math.floor(e.startTime / binMs), b = Math.floor(e.endTime / binMs);
        const at = channel * length, diff = channel * stride;
        fine.onsets[at + a]++;
        fine.cost[at + a] += e.cost ?? 0;
        if (e.status === 'error')
            fine.errors[at + Math.floor((0, model_js_1.errorOnsetOf)(e) / binMs)]++;
        const d = e.endTime - e.startTime, tokens = e.outputTokens ?? 0;
        if (d === 0) {
            fine.outputTokens[at + a] += tokens;
            continue;
        }
        if (a === b) {
            fine.activeMs[at + a] += d;
            fine.outputTokens[at + a] += tokens;
        }
        else {
            const left = (a + 1) * binMs - e.startTime, right = e.endTime - b * binMs, tokenRate = tokens / d;
            fine.activeMs[at + a] += left;

View on GitHub (pinned to 433685b202)