SG.dev
SG.dev3mo ago

Issue with Canceling a Scheduled Task – How to Retrieve and Pass Task ID to Frontend

I'm trying to implement a feature where users can schedule a deletion task and cancel it from the frontend, but I'm getting TS errors trying to return the taskId from a mutation:
export const scheduledDelete = mutation({
args: { id: v.id("thing") },
handler: async (ctx, args) => {
const userId = await getUserId(ctx);
const thing = await ctx.db.get(args.id);

if (!thing) throw new Error(`Dream with ID ${args.id} not found.`);
if (userId !== thing.userId) throw new Error("Unauthorized access.");

const taskId = await ctx.scheduler.runAfter(
10 * 1000,
internal.mutations.deleteThing,
{ id: thing._id }
);

return taskId;
},
});

export const deleteThing = internalMutation({
args: { id: v.id("thing") },
handler: async (ctx, args) => {
await ctx.db.delete(args.id);
},
});

export const cancelScheduledDeletion = mutation({
args: { taskId: v.id("_scheduled_functions") },
handler: async (ctx, args) => {
await ctx.scheduler.cancel(args.taskId);
},
});
export const scheduledDelete = mutation({
args: { id: v.id("thing") },
handler: async (ctx, args) => {
const userId = await getUserId(ctx);
const thing = await ctx.db.get(args.id);

if (!thing) throw new Error(`Dream with ID ${args.id} not found.`);
if (userId !== thing.userId) throw new Error("Unauthorized access.");

const taskId = await ctx.scheduler.runAfter(
10 * 1000,
internal.mutations.deleteThing,
{ id: thing._id }
);

return taskId;
},
});

export const deleteThing = internalMutation({
args: { id: v.id("thing") },
handler: async (ctx, args) => {
await ctx.db.delete(args.id);
},
});

export const cancelScheduledDeletion = mutation({
args: { taskId: v.id("_scheduled_functions") },
handler: async (ctx, args) => {
await ctx.scheduler.cancel(args.taskId);
},
});
'scheduledDelete' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
'handler' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
'scheduledDelete' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
'handler' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
- What is the recommended way to pass the scheduled task ID to the frontend so the user can cancel the task later? - Should the task ID be stored in the database instead, or is there a way to return it safely from the mutation? - Are there any examples or patterns recommended by the Convex team for handling cancelable scheduled tasks from the frontend?
2 Replies
Convex Bot
Convex Bot3mo ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
ballingt
ballingt3mo ago
This is an annoying one, we have some docs on this. You'll want a type annotation here
const taskId: Id<"_scheduled_functions"> = await ctx.scheduler.runAfter(
10 * 1000,
internal.mutations.deleteThing,
{ id: thing._id }
);
const taskId: Id<"_scheduled_functions"> = await ctx.scheduler.runAfter(
10 * 1000,
internal.mutations.deleteThing,
{ id: thing._id }
);

Did you find this page helpful?