authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-20 12:29:24-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-20 12:29:24-07:00
logbd8e3ee008e4c78085ebfbf47bf216389a92ac75
tree63c9fa1697a3fa8d931167505fb7aab682dc710e
parentc1d8a22f073e36768a8af5b0ef6472d802cbfa4d
signature Signed by PGP key B5690EEEBB952194

Align text generation error catches (#3292)

Co-authored-by: codex <codex@users.noreply.github.com>

5 files changed, 91 insertions(+), 114 deletions(-)

apps/server/src/textGeneration/ClaudeTextGeneration.ts+20-18
......@@ -234,28 +234,30 @@ export const makeClaudeTextGeneration = Effect.fn("makeClaudeTextGeneration")(fu
234234 );
235235
236236 const envelope = yield* decodeClaudeOutputEnvelope(rawStdout).pipe(
237 Effect.catchTag("SchemaError", (cause) =>
238 Effect.fail(
239 new TextGenerationError({
240 operation,
241 detail: "Claude CLI returned unexpected output format.",
242 cause,
243 }),
244 ),
245 ),
237 Effect.catchTags({
238 SchemaError: (cause) =>
239 Effect.fail(
240 new TextGenerationError({
241 operation,
242 detail: "Claude CLI returned unexpected output format.",
243 cause,
244 }),
245 ),
246 }),
246247 );
247248
248249 const decodeOutput = Schema.decodeEffect(outputSchemaJson);
249250 return yield* decodeOutput(envelope.structured_output).pipe(
250 Effect.catchTag("SchemaError", (cause) =>
251 Effect.fail(
252 new TextGenerationError({
253 operation,
254 detail: "Claude returned invalid structured output.",
255 cause,
256 }),
257 ),
258 ),
251 Effect.catchTags({
252 SchemaError: (cause) =>
253 Effect.fail(
254 new TextGenerationError({
255 operation,
256 detail: "Claude returned invalid structured output.",
257 cause,
258 }),
259 ),
260 }),
259261 );
260262 });
261263
apps/server/src/textGeneration/CodexTextGeneration.ts+10-9
......@@ -281,15 +281,16 @@ export const makeCodexTextGeneration = Effect.fn("makeCodexTextGeneration")(func
281281 }),
282282 ),
283283 Effect.flatMap(decodeOutput),
284 Effect.catchTag("SchemaError", (cause) =>
285 Effect.fail(
286 new TextGenerationError({
287 operation,
288 detail: "Codex returned invalid structured output.",
289 cause,
290 }),
291 ),
292 ),
284 Effect.catchTags({
285 SchemaError: (cause) =>
286 Effect.fail(
287 new TextGenerationError({
288 operation,
289 detail: "Codex returned invalid structured output.",
290 cause,
291 }),
292 ),
293 }),
293294 );
294295 }).pipe(Effect.ensuring(cleanup));
295296 });
apps/server/src/textGeneration/CursorTextGeneration.ts+27-40
......@@ -28,30 +28,7 @@ import {
2828
2929const CURSOR_TIMEOUT_MS = 180_000;
3030
31function mapCursorAcpError(
32 operation:
33 | "generateCommitMessage"
34 | "generatePrContent"
35 | "generateBranchName"
36 | "generateThreadTitle",
37 detail: string,
38 cause: unknown,
39): TextGenerationError {
40 return new TextGenerationError({
41 operation,
42 detail,
43 ...(cause !== undefined ? { cause } : {}),
44 });
45}
46
47function isTextGenerationError(error: unknown): error is TextGenerationError {
48 return (
49 typeof error === "object" &&
50 error !== null &&
51 "_tag" in error &&
52 error._tag === "TextGenerationError"
53 );
54}
31const isTextGenerationError = Schema.is(TextGenerationError);
5532
5633/**
5734 * Build a Cursor text-generation closure bound to a specific `CursorSettings`
......@@ -111,13 +88,14 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu
11188 model: modelSelection.model,
11289 selections: modelSelection.options,
11390 mapError: ({ cause, configId, step }) =>
114 mapCursorAcpError(
91 new TextGenerationError({
11592 operation,
116 step === "set-config-option"
117 ? `Failed to set Cursor ACP config option "${configId}" for text generation.`
118 : "Failed to set Cursor ACP base model for text generation.",
93 detail:
94 step === "set-config-option"
95 ? `Failed to set Cursor ACP config option "${configId}" for text generation.`
96 : "Failed to set Cursor ACP base model for text generation.",
11997 cause,
120 ),
98 }),
12199 });
122100
123101 return yield* runtime.prompt({
......@@ -140,7 +118,11 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu
140118 Effect.mapError((cause) =>
141119 isTextGenerationError(cause)
142120 ? cause
143 : mapCursorAcpError(operation, "Cursor ACP request failed.", cause),
121 : new TextGenerationError({
122 operation,
123 detail: "Cursor ACP request failed.",
124 cause,
125 }),
144126 ),
145127 );
146128
......@@ -157,21 +139,26 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu
157139
158140 const decodeOutput = Schema.decodeEffect(Schema.fromJsonString(outputSchemaJson));
159141 return yield* decodeOutput(extractJsonObject(rawResult)).pipe(
160 Effect.catchTag("SchemaError", (cause) =>
161 Effect.fail(
162 new TextGenerationError({
163 operation,
164 detail: "Cursor Agent returned invalid structured output.",
165 cause,
166 }),
167 ),
168 ),
142 Effect.catchTags({
143 SchemaError: (cause) =>
144 Effect.fail(
145 new TextGenerationError({
146 operation,
147 detail: "Cursor Agent returned invalid structured output.",
148 cause,
149 }),
150 ),
151 }),
169152 );
170153 }).pipe(
171154 Effect.mapError((cause) =>
172155 isTextGenerationError(cause)
173156 ? cause
174 : mapCursorAcpError(operation, "Cursor ACP text generation failed.", cause),
157 : new TextGenerationError({
158 operation,
159 detail: "Cursor ACP text generation failed.",
160 cause,
161 }),
175162 ),
176163 Effect.scoped,
177164 );
apps/server/src/textGeneration/GrokTextGeneration.ts+24-38
......@@ -31,30 +31,7 @@ import {
3131
3232const GROK_TIMEOUT_MS = 180_000;
3333
34function mapGrokAcpError(
35 operation:
36 | "generateCommitMessage"
37 | "generatePrContent"
38 | "generateBranchName"
39 | "generateThreadTitle",
40 detail: string,
41 cause: unknown,
42): TextGenerationError {
43 return new TextGenerationError({
44 operation,
45 detail,
46 ...(cause !== undefined ? { cause } : {}),
47 });
48}
49
50function isTextGenerationError(error: unknown): error is TextGenerationError {
51 return (
52 typeof error === "object" &&
53 error !== null &&
54 "_tag" in error &&
55 error._tag === "TextGenerationError"
56 );
57}
34const isTextGenerationError = Schema.is(TextGenerationError);
5835
5936export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(function* (
6037 grokSettings: GrokSettings,
......@@ -109,11 +86,11 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi
10986 currentModelId: currentGrokModelIdFromSessionSetup(started.sessionSetupResult),
11087 requestedModelId: resolvedModel,
11188 mapError: (cause) =>
112 mapGrokAcpError(
89 new TextGenerationError({
11390 operation,
114 "Failed to set Grok ACP base model for text generation.",
91 detail: "Failed to set Grok ACP base model for text generation.",
11592 cause,
116 ),
93 }),
11794 });
11895
11996 return yield* runtime.prompt({
......@@ -133,7 +110,11 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi
133110 Effect.mapError((cause: EffectAcpErrors.AcpError | TextGenerationError) =>
134111 isTextGenerationError(cause)
135112 ? cause
136 : mapGrokAcpError(operation, "Grok ACP request failed.", cause),
113 : new TextGenerationError({
114 operation,
115 detail: "Grok ACP request failed.",
116 cause,
117 }),
137118 ),
138119 );
139120
......@@ -150,21 +131,26 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi
150131
151132 const decodeOutput = Schema.decodeEffect(Schema.fromJsonString(outputSchemaJson));
152133 return yield* decodeOutput(extractJsonObject(trimmed)).pipe(
153 Effect.catchTag("SchemaError", (cause) =>
154 Effect.fail(
155 new TextGenerationError({
156 operation,
157 detail: "Grok Agent returned invalid structured output.",
158 cause,
159 }),
160 ),
161 ),
134 Effect.catchTags({
135 SchemaError: (cause) =>
136 Effect.fail(
137 new TextGenerationError({
138 operation,
139 detail: "Grok Agent returned invalid structured output.",
140 cause,
141 }),
142 ),
143 }),
162144 );
163145 }).pipe(
164146 Effect.mapError((cause) =>
165147 isTextGenerationError(cause)
166148 ? cause
167 : mapGrokAcpError(operation, "Grok ACP text generation failed.", cause),
149 : new TextGenerationError({
150 operation,
151 detail: "Grok ACP text generation failed.",
152 cause,
153 }),
168154 ),
169155 Effect.scoped,
170156 );
apps/server/src/textGeneration/OpenCodeTextGeneration.ts+10-9
......@@ -348,15 +348,16 @@ export const makeOpenCodeTextGeneration = Effect.fn("makeOpenCodeTextGeneration"
348348
349349 const decodeOutput = Schema.decodeEffect(Schema.fromJsonString(input.outputSchemaJson));
350350 return yield* decodeOutput(extractJsonObject(rawOutput)).pipe(
351 Effect.catchTag("SchemaError", (cause) =>
352 Effect.fail(
353 new TextGenerationError({
354 operation: input.operation,
355 detail: "OpenCode returned invalid structured output.",
356 cause,
357 }),
358 ),
359 ),
351 Effect.catchTags({
352 SchemaError: (cause) =>
353 Effect.fail(
354 new TextGenerationError({
355 operation: input.operation,
356 detail: "OpenCode returned invalid structured output.",
357 cause,
358 }),
359 ),
360 }),
360361 );
361362 });
362363