{"record":{"id":"a6be46def886e1c4","repo":"Hmbown/CodeWhale","slug":"codewhale-session-contains-no-journal-entries-or-messages","errorCode":null,"errorMessage":"Codewhale session contains no journal entries or messages.","messagePattern":"Codewhale session contains no journal entries or messages\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":2038,"sourceCode":"    const times = entries.map(e => parseTime(e.created_at)).filter((n) => n !== undefined);\n    if (times.length < 2)\n        return false;\n    const span = Math.max(...times) - Math.min(...times);\n    const envelope = created !== undefined && updated !== undefined ? updated - created : 0;\n    return envelope >= ENVELOPE_MIN_MS && span < COLLAPSED_SPAN_MS;\n}\nfunction pushEvent(events, event) {\n    events.push(event);\n}\nfunction fromCodewhaleSession(document, filename = 'Codewhale session', maxEvents = 250_000) {\n    const root = obj(document);\n    const metadata = obj(root.metadata);\n    const sessionId = str(metadata.id) ?? filename;\n    const journal = obj(root.journal);\n    const { entries, warnings } = activeJournalEntries(journal);\n    const sourceEntries = entries.length ? entries : (Array.isArray(root.messages) ? root.messages.map((message, i) => ({ id: `${sessionId}/message/${i}`, kind: 'message', message })) : []);\n    if (!sourceEntries.length)\n        throw new Error('Codewhale session contains no journal entries or messages.');\n    const created = parseTime(metadata.created_at);\n    const updated = parseTime(metadata.updated_at);\n    const orderOnly = collapsedTimestamps(sourceEntries, created, updated);\n    if (orderOnly) {\n        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.');\n    }\n    else {\n        const times = sourceEntries.map(e => parseTime(e.created_at)).filter((n) => n !== undefined);\n        if (!times.length)\n            warnings.push('Journal entries have no usable timestamps. The time axis is journal order.');\n    }\n    const events = [];\n    const pending = new Map();\n    let seq = 0;\n    const originWall = orderOnly ? undefined : sourceEntries.map(e => parseTime(e.created_at)).find((n) => n !== undefined);\n    const agentId = 'parent';\n    const model = str(metadata.model);\n    const provider = str(metadata.model_provider);","sourceCodeStart":2020,"sourceCodeEnd":2056,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L2020-L2056","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the session file actually contains conversation data: open it and confirm root.journal has entries or root.messages is a non-empty array.","Re-export the session from Codewhale after it has at least one user/assistant exchange.","Check activeJournalEntries filtering — entries may exist but be filtered out (e.g. inactive/invalid entries); inspect the warnings array returned with the parse.","Regenerate the session file if it was truncated by a crashed export or disk-full condition."],"exampleFix":"// before (empty snapshot)\n{ \"metadata\": { \"id\": \"s1\" }, \"journal\": { \"entries\": [] } }\n// after (valid snapshot)\n{ \"metadata\": { \"id\": \"s1\" }, \"journal\": { \"entries\": [ { \"id\": \"s1/entry/0\", \"kind\": \"user\", \"message\": { \"role\": \"user\", \"content\": [] }, \"text\": \"hello\" } ] } }","handlingStrategy":"validation","validationCode":"function importableSession(root) {\n  const entries = root?.journal?.entries ?? [];\n  const msgs = Array.isArray(root?.messages) ? root.messages : [];\n  return entries.length > 0 || msgs.length > 0;\n}","typeGuard":"const isSessionSnapshot = (v) => typeof v === 'object' && v !== null && ('journal' in v || 'messages' in v);","tryCatchPattern":"try { importSession(snapshot); } catch (e) { if (e.message.includes('no journal entries or messages')) skipEmptySession(snapshot); else throw e; }","preventionTips":["Only export sessions after at least one exchange has occurred.","Check journal.entries.length or messages.length before importing.","Inspect the warnings array from parsing for filtered-out entries.","Validate snapshot files against the export schema before import."],"tags":["import","session","empty-data"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}