{"record":{"id":"1290d2632ff4fda5","repo":"can1357/oh-my-pi","slug":"cannot-open-a-session-writer-before-a-session-file","errorCode":null,"errorMessage":"Cannot open a session writer before a session file exists","messagePattern":"Cannot open a session writer before a session file exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/session-manager.ts","lineNumber":790,"sourceCode":"\t\t\t\t\t\tnew Error(\"Authoritative session repair was superseded before verification.\"),\n\t\t\t\t\t]);\n\t\t\t\t}\n\t\t\t} while (this.#atomicRewriteDirty);\n\n\t\t\tthis.#fileIsCurrent = true;\n\t\t\tthis.#rewriteRequired = false;\n\t\t\tthis.#hasTitleSlot = true;\n\t\t\tthis.#clearDiskError();\n\t\t} catch (error) {\n\t\t\tif (error instanceof SessionPersistenceIndeterminateError) throw error;\n\t\t\tthrow this.#latchIndeterminate(operationError, [toError(error)]);\n\t\t} finally {\n\t\t\tif (this.#atomicRewriteFenceEpoch === epoch) this.#atomicRewriteFenceEpoch = null;\n\t\t}\n\t}\n\n\t#appendWriter(): SessionStorageWriter {\n\t\tif (!this.#sessionFile) throw new Error(\"Cannot open a session writer before a session file exists\");\n\n\t\tif (this.#writer?.isOpen()) return this.#writer;\n\n\t\tthis.#writer = this.#storage.openWriter(this.#sessionFile, {\n\t\t\tflags: \"a\",\n\t\t\tonError: err => this.#noteDiskFailure(err),\n\t\t});\n\t\treturn this.#writer;\n\t}\n\n\t#lineFor(entry: FileEntry): string {\n\t\treturn `${stringifyJson(prepareEntryForPersistence(entry, this.#blobs)) ?? \"null\"}\\n`;\n\t}\n\n\t#titleSlotLine(): string {\n\t\treturn serializeTitleSlot({\n\t\t\ttitle: this.#sessionName,\n\t\t\tsource: this.#titleSource,","sourceCodeStart":772,"sourceCodeEnd":808,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/session-manager.ts#L772-L808","documentation":"#appendWriter() lazily creates the storage writer used to append entries to the session file. It throws if #sessionFile is not yet set, because there is no file on disk to open a writer against. This is an internal invariant violation: writes must only happen after a session file has been created (new session persisted or a session resumed).","triggerScenarios":"Any append path (appendMessage, appendLabelChange, branch-related writes, etc.) invoked while #sessionFile is null — i.e. writing to a SessionManager that was constructed but whose session was never persisted to disk and never resumed from a file.","commonSituations":"Calling append APIs on a freshly constructed in-memory SessionManager before the first save/newSession creates the file; a previous disk failure cleared #sessionFile; SDK embedding where the caller skipped session creation.","solutions":["Create or resume a session so a session file exists before appending (call the new-session/save path first).","Check getSessionFile() before appending; if null, initialize the session.","If a disk failure cleared the session file, recover or re-create the session rather than writing.","Report a bug if the high-level API lets you append after a session was clearly created."],"exampleFix":"// before\nconst mgr = new SessionManager(...);\nmgr.appendMessage(msg, null); // throws\n// after\nconst mgr = new SessionManager(...);\nawait mgr.newSession(); // creates session file\nmgr.appendMessage(msg, null);","handlingStrategy":"validation","validationCode":"if (mgr.getSessionFile() == null) {\n  await mgr.newSession(); // or resume an existing file\n}","typeGuard":"function hasSessionFile(mgr) {\n  return mgr.getSessionFile() != null;\n}","tryCatchPattern":"null","preventionTips":["Always create or resume a session before calling any append API.","Assert getSessionFile() != null in wrapper code around the manager.","After a disk-failure callback, re-initialize the session before further writes."],"tags":["session","storage","invariant"],"backgroundTag":"session-file-not-initialized","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}