Hmbown/CodeWhale · error · Error
Invalid pet raster size.
Error message
Invalid pet raster size.
What it means
The `browser` tool is an action-dispatch wrapper: its `action` field must be one of start, status, navigate, click, type, screenshot, or stop. Any other value — including typos, casing variants, or compound actions — falls through the switch and throws, with the offending value embedded via JSON.stringify.
Solutions
- Use exactly one of: start, status, navigate, click, type, screenshot, stop — lowercase.
- Map unsupported intents to supported ones: 'close' → stop, 'open' → start or navigate, 'scroll' → key/mouse tools.
- Check the error message's `got ...` value to see what was actually passed (JSON.stringify of args.action).
Example fix
// before
resolveTool("browser", { action: "open", url: "https://example.com" });
// after
resolveTool("browser", { action: "navigate", url: "https://example.com" }); Defensive patterns
Strategy: try-catch
Validate before calling
const BROWSER_ACTIONS = ["start","status","navigate","click","type","screenshot","stop"];
if (!BROWSER_ACTIONS.includes(args.action)) {
throw new Error(`browser action must be one of ${BROWSER_ACTIONS.join(", ")}`);
} Type guard
const isBrowserAction = (a) => ["start","status","navigate","click","type","screenshot","stop"].includes(a);
Try / catch
try {
resolveTool("browser", args);
} catch (e) {
if (String(e.message).startsWith("browser action must be")) {
// log args.action and retry with a mapped/corrected action
} else throw e;
} Prevention
- Validate action against the enum list before calling; lowercase and normalize model output.
- Map foreign-API names: open→start/navigate, close→stop; scroll/back do not exist here.
- Read the `got ...` suffix in the message to see exactly what leaked through.
When it happens
Trigger: resolveTool("browser", {action:"open"}), {action:"Start"} (capitalized), {action:"close"} instead of "stop", {action:"scroll"} (not supported), or action missing entirely (undefined shows as undefined in the message).
Common situations: Model hallucinating browser actions from other automation APIs (scroll/back/forward); camelCase or capitalized action names from generated code; assuming a back/forward history action exists; passing the wrong tool's action set.
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
- Engine pet clock jump.
- Invalid Engine batch.
- Native habitat exceeds 8 MiB.
- No pet segment was prepared.
- Pet checkpoint exceeds its size limit.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/27a11f76498165fa.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/pet-native.js:120
}
advanceEngine(timeMs, motion, waiting) {
const target = Math.floor(timeMs * 30 / 1000 + 1e-8);
if (!Number.isFinite(timeMs) || target < this.engineTick || target - this.engineTick > 300)
throw new Error('Engine pet clock jump.');
this.engine.confirmWaiting(timeMs, waiting);
const voices = [];
while (this.engineTick < target) {
if ((this.engineTick + 1) % 12 === 0)
this.world.acceptTelemetry(this.engine.bucket(Math.floor(this.engineTick / 12)));
this.world.step(1 / 30, { motion, sensitivity: 1 });
voices.push(...this.world.voices);
this.engineTick++;
}
this.world.voices = voices;
}
terminal(width, height) {
if (![width, height].every(n => Number.isSafeInteger(n) && n >= 1 && n <= 512))
throw new Error('Invalid pet raster size.');
const { sim, frame } = this.world, l = (0, pet_sim_js_1.layout)(width * 2, height * 4, frame.state);
const cells = Array(width * height).fill(0), bits = [[1, 8], [2, 16], [4, 32], [64, 128]];
sim.p.forEach((p, i) => {
if (frame.state.observed < .92 && i % 2 === 1)
return;
const x = Math.round(l.ox + p.x * l.scale * l.flipX), y = Math.round(l.oy + p.y * l.scale);
if (x >= 0 && x < width * 2 && y >= 0 && y < height * 4)
cells[Math.floor(y / 4) * width + Math.floor(x / 2)] |= bits[y % 4][x % 2];
});
return JSON.stringify({ width, height, cells, timeMs: frame.timeMs, channel: frame.state.channel, arch: pet_sim_js_1.ARCH_OF[frame.state.channel],
hollow: frame.state.observed < .92, dozing: frame.behaviour === 'doze', lit: frame.state.lit });
}
pcmChannels(voicesJSON, startSample, length, rate) {
const p = (0, pet_audio_js_1.renderPetPCM)(JSON.parse(voicesJSON), startSample, length, rate);
return [p.left, p.right];
}
pcm(voicesJSON, startSample, length, rate) {
return JSON.stringify(this.pcmChannels(voicesJSON, startSample, length, rate).map(channel => Array.from(channel)));View on GitHub (pinned to 73e0f67d83)