{"record":{"id":"dc3263ce6bc02436","repo":"NousResearch/hermes-agent","slug":"expected-exported-session-json-or-jsonl","errorCode":null,"errorMessage":"Expected exported session JSON or JSONL","messagePattern":"Expected exported session JSON or JSONL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/lib/session-import.ts","lineNumber":21,"sourceCode":"export type ImportableSession = Record<string, unknown>;\n\nfunction normalizeImportSessions(value: unknown): ImportableSession[] {\n  const candidate =\n    value &&\n    typeof value === \"object\" &&\n    !Array.isArray(value) &&\n    Array.isArray((value as { sessions?: unknown }).sessions)\n      ? (value as { sessions: unknown[] }).sessions\n      : Array.isArray(value)\n        ? value\n        : [value];\n\n  const sessions = candidate.filter(\n    (item): item is ImportableSession =>\n      !!item && typeof item === \"object\" && !Array.isArray(item),\n  );\n  if (sessions.length !== candidate.length) {\n    throw new Error(\"Expected exported session JSON or JSONL\");\n  }\n  return sessions;\n}\n\nexport function parseImportSessions(text: string): ImportableSession[] {\n  const trimmed = text.trim();\n  if (!trimmed) throw new Error(\"File is empty\");\n\n  try {\n    return normalizeImportSessions(JSON.parse(trimmed));\n  } catch (jsonError) {\n    const lines = trimmed.split(/\\r?\\n/).filter((line) => line.trim());\n    if (lines.length <= 1) throw jsonError;\n    return normalizeImportSessions(lines.map((line) => JSON.parse(line)));\n  }\n}\n\nexport function importSummary(result: SessionImportResponse): string {","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/web/src/lib/session-import.ts#L3-L39","documentation":"normalizeImportSessions accepts (a) an array of session objects, (b) a single session object, or (c) a {sessions: [...]} export wrapper, and filters to plain objects. If any candidate item is null, a primitive, or an array, the counts diverge and this error is thrown — the file is neither a Hermes session export nor JSONL of session objects.","triggerScenarios":"Importing a JSON file whose top-level array mixes session objects with strings/numbers; a wrapped export where sessions contains nulls; JSONL lines that parse to scalars; pasting an arbitrary API response into the import box.","commonSituations":"Hand-editing an export and corrupting entries; importing the wrong file (config dump, log file); exports from a different tool with a similar but incompatible shape.","solutions":["Re-export sessions from the source dashboard/hermes instance and import that file unmodified.","Inspect the file: every element (or JSONL line) must be a JSON object representing one session.","If migrating from another tool, write a converter that emits an array of session objects (or {sessions: [...]}) first."],"exampleFix":"// before\n[{ \"id\": \"s1\" }, \"not-a-session\"]\n\n// after\n[{ \"id\": \"s1\" }, { \"id\": \"s2\" }]","handlingStrategy":"type-guard","validationCode":"const looksLikeSessionExport = (v: unknown): boolean => {\n  const items = Array.isArray(v)\n    ? v\n    : typeof v === 'object' && v !== null && Array.isArray((v as { sessions?: unknown }).sessions)\n      ? (v as { sessions: unknown[] }).sessions\n      : [v]\n  return items.length > 0 && items.every(i => !!i && typeof i === 'object' && !Array.isArray(i))\n}\nif (!looksLikeSessionExport(parsed)) { rejectFile('Not a Hermes session export'); return }","typeGuard":"function isPlainObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v)\n}","tryCatchPattern":"try {\n  const sessions = parseImportSessions(text)\n} catch (err) {\n  if (String(err) === 'Expected exported session JSON or JSONL') {\n    showError('File must contain session objects (JSON array, {sessions:[...]}, or JSONL)')\n    return\n  }\n  throw err\n}","preventionTips":["Only import files produced by the dashboard's own export.","Validate the shape before mutating any import state.","When converting from other tools, emit {sessions:[...]} with plain-object entries."],"tags":["import","json","sessions","validation"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}