danielmiessler/Fabric · warning · Error
Invalid session file format
Error message
Invalid session file format
What it means
Thrown by importFromFile in session-store.ts after a user-selected .json file was parsed successfully but the parsed value is not an array. The store's Message[] contract means any valid session export must be a JSON array of message objects; a JSON object, string, number, or null fails the Array.isArray check. This is a user-data validation error, not a parse error (readFileAsJson already succeeded).
Source
Thrown at web/src/lib/store/session-store.ts:83
try {
await saveToFile(messages, 'session-history.json');
toastService.success('Session exported successfully');
} catch (error) {
toastService.error('Failed to export session');
throw error;
}
},
async importFromFile(): Promise<Message[]> {
try {
const file = await openFileDialog('.json');
if (!file) {
throw new Error('No file selected');
}
const content = await readFileAsJson<Message[]>(file);
if (!Array.isArray(content)) {
throw new Error('Invalid session file format');
}
toastService.success('Session imported successfully');
return content;
} catch (error) {
toastService.error(error instanceof Error ? error.message : 'Failed to import session');
throw error;
}
},
async loadSessionMessages(sessionName: string): Promise<Message[]> {
try {
const response = await fetch(`/api/sessions/${sessionName}`);
if (!response.ok) {
throw new Error(`Failed to load session: ${response.statusText}`);
}
const data = await response.json();
const messages = Array.isArray(data.Message) ? data.Message : [];View on GitHub (pinned to 338b89cfe9)
Solutions
- Open the imported file and confirm the top-level value is a JSON array; if it is wrapped (e.g. {messages: [...]} or {Message: [...]}), unwrap it before importing
- Re-export a session from the app itself to get a known-good array-shaped file
- If exports legitimately vary, accept both shapes: use Array.isArray(content) ? content : Array.isArray(content?.messages ?? content?.Message) ? (content.messages ?? content.Message) : null and only throw when both fail
- Add per-item validation so a non-Message array element fails early with a clear message instead of breaking render later
Example fix
// before
const content = await readFileAsJson<Message[]>(file);
if (!Array.isArray(content)) {
throw new Error('Invalid session file format');
}
// after
const content = await readFileAsJson<unknown>(file);
const messages = Array.isArray(content)
? content
: Array.isArray((content as any)?.messages)
? (content as any).messages
: Array.isArray((content as any)?.Message)
? (content as any).Message
: null;
if (!messages || !messages.every(m => m && typeof m.role === 'string')) {
throw new Error('Invalid session file format: expected an array of messages');
} Defensive patterns
Strategy: validation
Validate before calling
// Run before accepting the file content
import { readFileAsJson } from './file-utils';
async function parseSessionFile(file: File): Promise<Message[]> {
const content = await readFileAsJson<unknown>(file);
const candidate = Array.isArray(content)
? content
: Array.isArray((content as Record<string, unknown>)?.messages)
? (content as { messages: unknown[] }).messages
: null;
if (!candidate) throw new Error('Invalid session file format: expected an array of messages');
return candidate as Message[];
} Type guard
function isMessageArray(v: unknown): v is Message[] {
return (
Array.isArray(v) &&
v.every(
(m) =>
m !== null &&
typeof m === 'object' &&
typeof (m as Message).role === 'string' &&
typeof (m as Message).content === 'string'
)
);
} Try / catch
try {
const messages = await parseSessionFile(file);
toastService.success('Session imported successfully');
return messages;
} catch (error) {
toastService.error(error instanceof Error ? error.message : 'Failed to import session');
return []; // do not rethrow into UI event handlers that don't catch
} Prevention
- Always export sessions from the app itself so the file shape matches the importer
- Validate shape (isMessageArray) before assigning to stores, not after render
- Keep the export format documented and versioned so future shape changes are intentional
When it happens
Trigger: Selecting a file that is valid JSON but not an array: a single exported message object ({"role":"user",...}), a wrapped export like {"messages":[...]}, an empty JSON file parsed as null, or a config file accidentally picked in the .json file dialog.
Common situations: Hand-edited exports, exports from a different app version that wrapped messages in an envelope, picking the wrong file (pattern config, package.json), or an export that contains {Message: [...]} from the server API shape rather than the client's bare-array shape.
Related errors
- Invalid response format: missing vendors data
- ${fileName} contains no machine-readable text. OCR is necess
- ${fileName}: the conversion returned no text.
- Failed to load session: ${response.statusText}
- Pattern validation failed: {validation_message}
AI-assisted analysis of danielmiessler/Fabric@338b89cfe9 (2026-08-15).
Data as JSON: /api/errors/5a31d5c75fb0fa6a.
Report an issue: GitHub.