Hmbown/CodeWhale · error · Error

Choose one recording to import.

Error message

Choose one recording to import.

What it means

Thrown during PET replay import when the selected file is parsed as a trace recording and `importTrace` returns a number of traces other than exactly one. The import path supports JSONL replay tapes and single-recording trace exports; a file containing zero or multiple recordings is ambiguous and the library refuses to guess.

Solutions

  1. Export a single recording from the trace tool and re-import that file.
  2. Verify the file is non-empty and contains exactly one trace recording.
  3. If importing a replay, use the JSONL tape format with a version-1 header containing `simTimeMs`.

Example fix

// before: importing a combined multi-trace export
throw new Error('Choose one recording to import.');
// after: filter to a single trace client-side first
const traces = importTrace(text, file.name, { privacy: 'metadata' });
if (traces.length !== 1) { /* pick traces[0] explicitly or re-export a single recording */ }
Defensive patterns

Strategy: validation

Validate before calling

const traces = importTrace(text, file.name, { privacy: 'metadata' });
if (traces.length !== 1) { alert('Please choose a file containing exactly one recording.'); return; }

Type guard

const isSingleTrace = (t: { length: number }) => t.length === 1;

Try / catch

try { /* import */ } catch (e) { if (e.message.includes('Choose one recording')) showMessage('Select a single-recording file.'); }

Prevention

When it happens

Trigger: Calling the import handler (file picker `onchange`) with a text file that is not a version-1 JSONL tape (`simTimeMs` header missing) and whose contents `importTrace(text, file.name, { privacy: 'metadata' })` yields an array of length != 1.

Common situations: User picks a multi-recording combined export, an empty or truncated trace file, or an unrelated JSON/text file that is not a pet replay.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at pet/src/ui/pet.ts:200

  interactions = []; seek.max = mode.value === 'demo' ? '80' : '120';
  source.textContent = mode.value === 'demo' ? 'Event demo · synthetic telemetry' : 'Wild · simulated creature'; rebuild();
  message.textContent = mode.value === 'demo' ? 'Synthetic event-v1 telemetry uses the same derivation as imported traces.' : 'Wild mode is a simulated creature. Import event-v1, OTLP, or Codewhale telemetry to see work.';
};
get<HTMLInputElement>('file').onchange = async event => {
  const input = event.target as HTMLInputElement, file = input.files?.[0]; if (!file) return;
  try {
    if (file.size > 64 * 1024 * 1024) throw new Error('Pet import exceeds 64 MiB.');
    const text = await file.text(); let replay: unknown;
    try { replay = JSON.parse(text); } catch { /* JSONL and trace imports follow below. */ }
    let next: PetWorld;
    if (replay && typeof replay === 'object' && 'petReplayVersion' in replay) next = PetWorld.fromRecording(points, replay);
    else {
      let nextTape: readonly PetBucket[];
      const first = replay ?? JSON.parse(text.split(/\r?\n/).find(line => line.trim()) || '{}');
      if (first && typeof first === 'object' && 'version' in first && first.version === 1 && 'simTimeMs' in first) nextTape = decodePetJSONL(text);
      else {
        const traces = importTrace(text, file.name, { privacy: 'metadata' });
        if (traces.length !== 1) throw new Error('Choose one recording to import.');
        nextTape = compilePetTelemetry(traces[0].events, traces[0].duration);
      }
      next = new PetWorld(points, nextTape, [], 2, true);
    }
    if (!await mayLeave()) return;
    adoptImported(next, `Local replay · ${file.name}`);
    message.textContent = 'Recording loaded locally, including its current pose, interactions and score.';
  } catch (error) { message.textContent = error instanceof Error ? error.message : 'Unable to import this file.'; }
  finally { input.value = ''; }
};
get('save').onclick = () => {
  try {
    const chunks: string[] = []; let bytes = 0;
    for (let index = 0; ; index++) {
      const chunk = world.recordingChunk(index); if (chunk === null) break;
      bytes += new TextEncoder().encode(chunk).length;
      if (bytes > 64 * 1024 * 1024) throw new Error('Recording exceeds the 64 MiB export limit. The current world was kept.');
      chunks.push(chunk);

View on GitHub (pinned to 433685b202)