#returning using ctx.runQuery gives me type error while db.query doesnt

3 messages · Page 1 of 1 (latest)

polar oliveBOT
#

Hi @spiral wadi. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

This is a well-known TypeScript issue in Convex called "circular type inference." When you use ctx.runQuery (or ctx.runMutation, ctx.runAction, etc.) and return or use the result in a way that TypeScript tries to infer the return type of your function from the result of another Convex function, it can create a circular dependency in the type system. This leads to the error:

'submitRequest' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)

When you use ctx.db.query directly, TypeScript can infer the types from your schema without this circularity, so you don't get the error.

How to fix it:
You need to break the circular inference by explicitly annotating either:

  1. The return type of your handler function, or
  2. The type of the value you get from ctx.runQuery.

For example:

export const submitRequest = mutation({
  args: { /* ... */ },
  handler: async (ctx, args): Promise<Id<"requests">> => {
    const dbUser = await ctx.runQuery(internal.users.getUserByExternalId, {
      externalId: user.subject,
    });

    if (!dbUser) throw new Error("User not found");

    const newId = await ctx.db.insert("requests", {
      spotId,
      userId: dbUser._id,
      status: "active",
      startingDate,
      message,
    });

    return newId;
  },
});

Or, annotate the result of ctx.runQuery:

const dbUser: Doc<"users"> | null = await ctx.runQuery(internal.users.getUserByExternalId, {
  externalId: user.subject,
});

This tells TypeScript exactly what type to expect, breaking the circular reference and resolving the error. You can read more about this in the Convex documentation: Dealing with circular type inference.

#

This is a common pattern in Convex, and the workaround is to always type the handler return or the result of ctx.run* calls when you encounter this error. You do not lose type safety by doing this, as TypeScript will still check that your annotation matches the actual return type of the called function.
Dealing with circular type inference

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: