fix: use mapError instead of orDie for context snapshot decoding (#30905)
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
This commit is contained in:
parent
a136caa1b3
commit
a261b55e43
@ -92,7 +92,7 @@ export class OperationUnavailableError extends Schema.TaggedErrorClass<Operation
|
||||
},
|
||||
) {}
|
||||
|
||||
export { MessageDecodeError } from "./session/error"
|
||||
export { ContextSnapshotDecodeError, MessageDecodeError } from "./session/error"
|
||||
|
||||
export class PromptConflictError extends Schema.TaggedErrorClass<PromptConflictError>()("Session.PromptConflictError", {
|
||||
sessionID: SessionSchema.ID,
|
||||
|
||||
@ -7,6 +7,7 @@ import { EventV2 } from "../event"
|
||||
import { Location } from "../location"
|
||||
import { SystemContext } from "../system-context"
|
||||
import { SystemContextRegistry } from "../system-context-registry"
|
||||
import { ContextSnapshotDecodeError } from "./error"
|
||||
import { SessionEvent } from "./event"
|
||||
import { SessionInput } from "./input"
|
||||
import { SessionMessageID } from "./message-id"
|
||||
@ -49,7 +50,7 @@ export function prepare(
|
||||
context: SystemContextRegistry.Interface,
|
||||
sessionID: SessionSchema.ID,
|
||||
location: Location.Ref,
|
||||
): Effect.Effect<Prepared, SystemContext.InitializationBlocked> {
|
||||
): Effect.Effect<Prepared, SystemContext.InitializationBlocked | ContextSnapshotDecodeError> {
|
||||
return retryRevisionMismatch(() => prepareOnce(db, events, context, sessionID, location)).pipe(
|
||||
Effect.withSpan("SessionContextEpoch.prepare"),
|
||||
)
|
||||
@ -69,7 +70,9 @@ const prepareOnce = Effect.fnUntraced(function* (
|
||||
return { baseline: generation.baseline, baselineSeq }
|
||||
}
|
||||
|
||||
const snapshot = yield* Schema.decodeUnknownEffect(SystemContext.Snapshot)(stored.snapshot).pipe(Effect.orDie)
|
||||
const snapshot = yield* Schema.decodeUnknownEffect(SystemContext.Snapshot)(stored.snapshot).pipe(
|
||||
Effect.mapError((error) => new ContextSnapshotDecodeError({ sessionID, details: String(error) })),
|
||||
)
|
||||
const result =
|
||||
stored.replacement_seq === null
|
||||
? yield* SystemContext.reconcile(value, snapshot)
|
||||
|
||||
@ -6,3 +6,15 @@ export class MessageDecodeError extends Schema.TaggedErrorClass<MessageDecodeErr
|
||||
sessionID: SessionSchema.ID,
|
||||
messageID: SessionMessage.ID,
|
||||
}) {}
|
||||
|
||||
export class ContextSnapshotDecodeError extends Schema.TaggedErrorClass<ContextSnapshotDecodeError>()(
|
||||
"Session.ContextSnapshotDecodeError",
|
||||
{
|
||||
sessionID: SessionSchema.ID,
|
||||
details: Schema.String,
|
||||
},
|
||||
) {
|
||||
override get message() {
|
||||
return `Failed to decode context snapshot for session ${this.sessionID}: ${this.details}`
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ export * as SessionRunner from "./index"
|
||||
import type { LLMError } from "@opencode-ai/llm"
|
||||
import { Context, Effect, Schema } from "effect"
|
||||
import { SessionSchema } from "../schema"
|
||||
import type { MessageDecodeError } from "../error"
|
||||
import type { ContextSnapshotDecodeError, MessageDecodeError } from "../error"
|
||||
import { SessionRunnerModel } from "./model"
|
||||
import type { SystemContext } from "../../system-context"
|
||||
|
||||
@ -19,6 +19,7 @@ export type RunError =
|
||||
| LLMError
|
||||
| SessionRunnerModel.Error
|
||||
| MessageDecodeError
|
||||
| ContextSnapshotDecodeError
|
||||
| StepLimitExceededError
|
||||
| SystemContext.InitializationBlocked
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ import { ProjectTable } from "@opencode-ai/core/project/sql"
|
||||
import { QuestionV2 } from "@opencode-ai/core/question"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { SessionV2 } from "@opencode-ai/core/session"
|
||||
import { ContextSnapshotDecodeError } from "@opencode-ai/core/session/error"
|
||||
import { SessionEvent } from "@opencode-ai/core/session/event"
|
||||
import { SessionInput } from "@opencode-ai/core/session/input"
|
||||
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||
@ -631,6 +632,31 @@ describe("SessionRunnerLLM", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("fails gracefully when a stored context snapshot cannot be decoded", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setup
|
||||
const session = yield* SessionV2.Service
|
||||
const { db } = yield* Database.Service
|
||||
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "First" }), resume: false })
|
||||
response = []
|
||||
yield* session.resume(sessionID)
|
||||
yield* db
|
||||
.update(SessionContextEpochTable)
|
||||
.set({ snapshot: { invalid: { value: "bad" } } })
|
||||
.where(eq(SessionContextEpochTable.session_id, sessionID))
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
yield* session.prompt({ sessionID, prompt: new Prompt({ text: "Second" }), resume: false })
|
||||
requests.length = 0
|
||||
|
||||
const exit = yield* session.resume(sessionID).pipe(Effect.exit)
|
||||
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(ContextSnapshotDecodeError)
|
||||
expect(requests).toHaveLength(0)
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("does not create a source Location epoch after a concurrent Session move", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setup
|
||||
|
||||
Loading…
Reference in New Issue
Block a user