{"record":{"id":"c981ff40147f6ecf","repo":"Hmbown/CodeWhale","slug":"codewhale-session-contains-no-journal-entries-or-messages-c981ff","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":"pet/ios/Resources/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/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/ios/Resources/pet-native.js#L2020-L2056","documentation":"When loading a Codewhale session for the pet view, the loader reads root.journal entries, falling back to root.messages. If neither yields any entries, it throws 'Codewhale session contains no journal entries or messages.' because there is nothing to render on the pet timeline.","triggerScenarios":"Opening a session file whose JSON has no journal object with active entries and no messages array — e.g. an empty/new session, a truncated or corrupted file, or a file from an incompatible format where entries exist under a different key.","commonSituations":"Pointing the importer at the wrong file (a metadata-only stub), a session wiped by a crash before the first write, schema drift where journal entries are nested differently, or all journal entries filtered out by activeJournalEntries (e.g. all marked deleted).","solutions":["Verify the session file actually contains journal.entries or messages and pick a non-empty session","Inspect why activeJournalEntries filtered everything out (deleted/pruned entries) and relax or fix the filter","Check the file format/version — if entries live under another key, update the loader or convert the file","Regenerate or re-export the session from the source app"],"exampleFix":"// before\nloadSession('session-empty.json');\n// after\nconst data = JSON.parse(fs.readFileSync('session-empty.json', 'utf8'));\nconst hasEntries = data.journal && Object.keys(data.journal).length > 0;\nconst hasMessages = Array.isArray(data.messages) && data.messages.length > 0;\nif (hasEntries || hasMessages) loadSession('session-empty.json');\nelse console.warn('session has no content, skipping');","handlingStrategy":"try-catch","validationCode":"function sessionHasContent(root) {\n  const journalEntries = root?.journal ? Object.keys(root.journal).length : 0;\n  const messages = Array.isArray(root?.messages) ? root.messages.length : 0;\n  return journalEntries > 0 || messages > 0;\n}","typeGuard":null,"tryCatchPattern":"try {\n  loadSession(file);\n} catch (err) {\n  if (err.message.includes('no journal entries or messages')) {\n    console.warn(`skipping empty session ${file}`);\n  } else throw err;\n}","preventionTips":["Pre-scan session files for non-empty journal/messages before import","Check activeJournalEntries filters (deleted/pruned entries) when counts look wrong","Handle empty new sessions as a normal case in batch importers","Validate session file schema/version before parsing"],"tags":["empty-data","session","import"],"backgroundTag":"empty-result-set","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}