Hmbown/CodeWhale · error · Error

Codewhale session contains no journal entries or messages.

Error message

Codewhale session contains no journal entries or messages.

What it means

This error is thrown while importing a Codewhale session snapshot (JSON) into the native pet watch timeline. The importer reads the session's journal entries, falling back to raw messages, and requires at least one source entry to build timeline events. A snapshot with an empty journal, no valid active entries, and no messages array is considered unusable, so the import fails loudly rather than producing an empty timeline.

Solutions

  1. Verify the session file actually contains conversation data: open it and confirm root.journal has entries or root.messages is a non-empty array.
  2. Re-export the session from Codewhale after it has at least one user/assistant exchange.
  3. Check activeJournalEntries filtering — entries may exist but be filtered out (e.g. inactive/invalid entries); inspect the warnings array returned with the parse.
  4. Regenerate the session file if it was truncated by a crashed export or disk-full condition.

Example fix

// before (empty snapshot)
{ "metadata": { "id": "s1" }, "journal": { "entries": [] } }
// after (valid snapshot)
{ "metadata": { "id": "s1" }, "journal": { "entries": [ { "id": "s1/entry/0", "kind": "user", "message": { "role": "user", "content": [] }, "text": "hello" } ] } }
Defensive patterns

Strategy: validation

Validate before calling

function importableSession(root) {
  const entries = root?.journal?.entries ?? [];
  const msgs = Array.isArray(root?.messages) ? root.messages : [];
  return entries.length > 0 || msgs.length > 0;
}

Type guard

const isSessionSnapshot = (v) => typeof v === 'object' && v !== null && ('journal' in v || 'messages' in v);

Try / catch

try { importSession(snapshot); } catch (e) { if (e.message.includes('no journal entries or messages')) skipEmptySession(snapshot); else throw e; }

Prevention

When it happens

Trigger: Calling the session import with a JSON file whose root object has a journal with zero active entries (all filtered out by activeJournalEntries) and either no root.messages array or an empty one.

Common situations: Importing a newly created or truncated session file; exporting a session before any messages were recorded; passing a metadata-only or malformed export; importing a file from a different tool that happens to share the schema shape but has no conversation content.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/a6be46def886e1c4. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tui/pet_watch/pet-native.js:2038

    const times = entries.map(e => parseTime(e.created_at)).filter((n) => n !== undefined);
    if (times.length < 2)
        return false;
    const span = Math.max(...times) - Math.min(...times);
    const envelope = created !== undefined && updated !== undefined ? updated - created : 0;
    return envelope >= ENVELOPE_MIN_MS && span < COLLAPSED_SPAN_MS;
}
function pushEvent(events, event) {
    events.push(event);
}
function fromCodewhaleSession(document, filename = 'Codewhale session', maxEvents = 250_000) {
    const root = obj(document);
    const metadata = obj(root.metadata);
    const sessionId = str(metadata.id) ?? filename;
    const journal = obj(root.journal);
    const { entries, warnings } = activeJournalEntries(journal);
    const sourceEntries = entries.length ? entries : (Array.isArray(root.messages) ? root.messages.map((message, i) => ({ id: `${sessionId}/message/${i}`, kind: 'message', message })) : []);
    if (!sourceEntries.length)
        throw new Error('Codewhale session contains no journal entries or messages.');
    const created = parseTime(metadata.created_at);
    const updated = parseTime(metadata.updated_at);
    const orderOnly = collapsedTimestamps(sourceEntries, created, updated);
    if (orderOnly) {
        warnings.push('Journal created_at values are collapsed to last-save time, not execution time. The time axis is journal order (1 ms per emitted event), not wall-clock duration. Gap, burst, and cycle-period findings are not execution-time claims.');
    }
    else {
        const times = sourceEntries.map(e => parseTime(e.created_at)).filter((n) => n !== undefined);
        if (!times.length)
            warnings.push('Journal entries have no usable timestamps. The time axis is journal order.');
    }
    const events = [];
    const pending = new Map();
    let seq = 0;
    const originWall = orderOnly ? undefined : sourceEntries.map(e => parseTime(e.created_at)).find((n) => n !== undefined);
    const agentId = 'parent';
    const model = str(metadata.model);
    const provider = str(metadata.model_provider);

View on GitHub (pinned to 73e0f67d83)