Summary
When VS Code is killed during a streaming chat response, the session .jsonl contains a structurally valid but semantically incomplete entry: a kind:2 (Set) record with
equestId present but no
esponse key. On the next launch, ChatSessionStore crashes with TypeError: Cannot read properties of undefined (reading 'response') and permanently discards the entire session.
Versions
- VS Code: 1.113.0
- Copilot Chat: 0.42.2
- OS: Windows 11 (x64)
Steps to Reproduce
- Open a Copilot Chat session in agent mode
- Send a message and let the response start streaming
- While the response is in progress, kill VS Code (End Task / power loss)
- Reopen VS Code and open the same workspace
- Open Chat History and click the session
Expected: Session loads, showing at minimum the turns that completed before the interruption.
Actual: Session never loads. The history entry is visible but clicking it stays on the blank new-chat screen.
Root Cause
chatSessionStore.ts reads the .jsonl via ObjectMutationLog.read(), which replays all entries unconditionally. An interrupted in-progress response leaves the assembled
equests array containing an entry with
equestId and message but no
esponse property.
The revival loop at chatSessionStore.ts ~L674 then does:
ypescript for (const request of session.requests) { if (Array.isArray(request.response)) { // false (request.response is undefined) request.response = request.response.map(.); // throws if reached } else if (typeof request.response === 'string') { // false . } // silently falls through - but... }
The Array.isArray(undefined) and ypeof undefined === 'string' both return false, so execution falls through without throwing here. The actual crash occurs downstream at
ormalizeSerializableChatData() or during schema traversal in objectMutationLog.ts, which calls
ethrowWithPathSegment to annotate the path as .requests[N].response: Cannot read properties of undefined.
The entire exception is caught at the outer catch block (~L689):
ypescript } catch (err) { this.reportError('malformedSession', Malformed session data in : ., err); return undefined; }
This logs the error and returns undefined, making the session permanently unloadable.
Confirmed via
enderer.log:
[error] ChatSessionStore: Malformed session data in ./chatSessions/ce00ad01-..jsonl: [{"kind":0,"v":{"vers...}] Cannot read properties of undefined (reading 'response')
The same error repeats ~10 times (one per session-poll interval) then stops.
Suggested Fix
Add a null/undefined guard before accessing .response on assembled incomplete requests, and filter out requests with no response from the revived session rather than crashing:
ypescript for (const request of session.requests) { if (request.response === undefined || request.response === null) { continue; // skip incomplete in-flight requests from interrupted sessions } if (Array.isArray(request.response)) { . } else if (typeof request.response === 'string') { . } }
Alternatively, ObjectMutationLog.read() could detect and skip incomplete entries (those lacking a sealed marker) at the log-replay layer.
Cross-reference
Related to #296890 (concurrent-write JSONL corruption causing the same Malformed session data symptom, but different root cause - interleaved bytes vs. semantically incomplete JSON). Both issues hit the same catch block.
Summary
When VS Code is killed during a streaming chat response, the session .jsonl contains a structurally valid but semantically incomplete entry: a kind:2 (Set) record with
equestId present but no
esponse key. On the next launch, ChatSessionStore crashes with TypeError: Cannot read properties of undefined (reading 'response') and permanently discards the entire session.
Versions
Steps to Reproduce
Expected: Session loads, showing at minimum the turns that completed before the interruption.
Actual: Session never loads. The history entry is visible but clicking it stays on the blank new-chat screen.
Root Cause
chatSessionStore.ts reads the .jsonl via ObjectMutationLog.read(), which replays all entries unconditionally. An interrupted in-progress response leaves the assembled
equests array containing an entry with
equestId and message but no
esponse property.
The revival loop at chatSessionStore.ts ~L674 then does:
ypescript for (const request of session.requests) { if (Array.isArray(request.response)) { // false (request.response is undefined) request.response = request.response.map(.); // throws if reached } else if (typeof request.response === 'string') { // false . } // silently falls through - but... }The Array.isArray(undefined) and ypeof undefined === 'string' both return false, so execution falls through without throwing here. The actual crash occurs downstream at
ormalizeSerializableChatData() or during schema traversal in objectMutationLog.ts, which calls
ethrowWithPathSegment to annotate the path as .requests[N].response: Cannot read properties of undefined.
The entire exception is caught at the outer catch block (~L689):
ypescript } catch (err) { this.reportError('malformedSession', Malformed session data in : ., err); return undefined; }This logs the error and returns undefined, making the session permanently unloadable.
Confirmed via
enderer.log:
[error] ChatSessionStore: Malformed session data in ./chatSessions/ce00ad01-..jsonl: [{"kind":0,"v":{"vers...}] Cannot read properties of undefined (reading 'response')The same error repeats ~10 times (one per session-poll interval) then stops.
Suggested Fix
Add a null/undefined guard before accessing .response on assembled incomplete requests, and filter out requests with no response from the revived session rather than crashing:
ypescript for (const request of session.requests) { if (request.response === undefined || request.response === null) { continue; // skip incomplete in-flight requests from interrupted sessions } if (Array.isArray(request.response)) { . } else if (typeof request.response === 'string') { . } }Alternatively, ObjectMutationLog.read() could detect and skip incomplete entries (those lacking a sealed marker) at the log-replay layer.
Cross-reference
Related to #296890 (concurrent-write JSONL corruption causing the same Malformed session data symptom, but different root cause - interleaved bytes vs. semantically incomplete JSON). Both issues hit the same catch block.