Skip to content

Commit 05e626a

Browse files
authored
Add user stack track to error reporting (#218)
pass through RunResult to improve error line tracking add example of catching an error Fixes #167 <!-- end of auto-generated comment: release notes by coderabbit.ai --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added example workflow demonstrating error catching and automatic retry mechanisms for failed operations. * **Improvements** * Enhanced workflow step execution with improved error handling and explicit result state interpretation. * **Tests** * Added comprehensive test coverage for workflow step execution, error propagation, and cancellation scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2 parents 41070d1 + 2fb3c68 commit 05e626a

File tree

5 files changed

+408
-20
lines changed

5 files changed

+408
-20
lines changed

example/convex/_generated/api.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
import type * as admin from "../admin.js";
12+
import type * as catchError from "../catchError.js";
1213
import type * as example from "../example.js";
1314
import type * as nestedWorkflow from "../nestedWorkflow.js";
1415
import type * as passingSignals from "../passingSignals.js";
@@ -23,6 +24,7 @@ import type {
2324

2425
declare const fullApi: ApiFromModules<{
2526
admin: typeof admin;
27+
catchError: typeof catchError;
2628
example: typeof example;
2729
nestedWorkflow: typeof nestedWorkflow;
2830
passingSignals: typeof passingSignals;

example/convex/catchError.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { v } from "convex/values";
2+
import { WorkflowId } from "@convex-dev/workflow";
3+
import { internal } from "./_generated/api.js";
4+
import { internalAction, internalMutation } from "./_generated/server.js";
5+
import { workflow } from "./example.js";
6+
7+
export const alwaysFails = internalAction({
8+
args: {},
9+
returns: v.null(),
10+
handler: async (_ctx, _args) => {
11+
throw new Error("This action always fails for testing");
12+
},
13+
});
14+
15+
export const catchErrorWorkflow = workflow.define({
16+
args: { manualRetries: v.number() },
17+
returns: v.number(),
18+
handler: async (step, args): Promise<number> => {
19+
let i;
20+
for (i = 0; i < args.manualRetries + 1; i++) {
21+
try {
22+
await step.runAction(internal.catchError.alwaysFails, {});
23+
return i;
24+
} catch (e) {
25+
if (e instanceof Error) {
26+
console.error(e.name, e.message, e.stack);
27+
} else {
28+
console.error("Caught error in workflow handler:", e);
29+
}
30+
}
31+
}
32+
return i;
33+
},
34+
});
35+
36+
export const start = internalMutation({
37+
args: { manualRetries: v.optional(v.number()) },
38+
returns: v.string(),
39+
handler: async (ctx, args) => {
40+
const id: WorkflowId = await workflow.start(
41+
ctx,
42+
internal.catchError.catchErrorWorkflow,
43+
{ manualRetries: args.manualRetries ?? 0 },
44+
);
45+
return id;
46+
},
47+
});

src/client/step.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ export type StepRequest = {
4444
retry: RetryBehavior | boolean | undefined;
4545
schedulerOptions: SchedulerOptions;
4646

47-
resolve: (result: unknown) => void;
48-
reject: (error: unknown) => void;
47+
resolve: (result: RunResult) => void;
4948
};
5049

5150
export class StepExecutor {
@@ -140,17 +139,7 @@ export class StepExecutor {
140139
`Assertion failed: no outcome for completed function call`,
141140
);
142141
}
143-
switch (entry.step.runResult.kind) {
144-
case "success":
145-
message.resolve(entry.step.runResult.returnValue);
146-
break;
147-
case "failed":
148-
message.reject(new Error(entry.step.runResult.error));
149-
break;
150-
case "canceled":
151-
message.reject(new Error("Canceled"));
152-
break;
153-
}
142+
message.resolve(entry.step.runResult);
154143
}
155144

156145
async startSteps(messages: StepRequest[]): Promise<JournalEntry[]> {

0 commit comments

Comments
 (0)