#Adjusting CORS bricks deployment

2 messages · Page 1 of 1 (latest)

true stream
#

I think one of my dev deployments got wedged in the HTTP actions layer.

Project: autotcg
Broken dev deployment: healthy-guineapig-155
Working replacement dev deployment: rightful-partridge-886

Symptoms on healthy-guineapig-155:

  • Every HTTP action returned generic 500:
    {"code":"InternalServerError","message":"Your request couldn't be completed. Try again later."}
  • This happened for all Better Auth endpoints under /api/auth/*
  • I then replaced convex/http.ts with a single bare route:
    GET /_debug-auth -> Response.json({ ok: true })
  • function-spec confirmed only /_debug-auth was registered, but it still returned the same generic 500.
  • Normal Convex query/mutation execution was not globally broken. A normal query reached app code and returned my
    app’s expected auth error.
  • Dashboard logs showed only the generic message, no stack trace, no handler logs, and ~single-digit ms execution.

Sample old request ID:
fb221cbef4b3553b

I created a fresh dev deployment in the same project, rightful-partridge-886, copied env vars, pushed the same
code, and HTTP actions immediately worked again. Better Auth sign-in/sign-out works there.

But now I made some adjustments and proved the same circumstances bricked another deployment.

This is with KitCN + Better-auth.

Question: can someone inspect internal logs for healthy-guineapig-155 and see why HTTP actions return generic 500
before handler execution?

#

These were the changes:

Update(apps\web\convex\auth.ts)
  ⎿  Added 2 lines, removed 1 line
      83      },
      84    },
      85    telemetry: { enabled: false },
      86 -  trustedOrigins: [process.env.SITE_URL!],
      86 +  // Includes sentinel WebView dev origin (Tauri 2 default port 1420).
      87 +  trustedOrigins: [process.env.SITE_URL!, 'http://localhost:1420'],
      88  }));

● Update(apps\web\convex\http.ts)
  ⎿  Added 1 line, removed 1 line
       6
       7  registerRoutes(http, getAuth, {
       8    cors: {
       9 -    allowedOrigins: [process.env.SITE_URL!],
       9 +    allowedOrigins: [process.env.SITE_URL!, 'http://localhost:1420'],
      10    },
      11  });
      12

● Write(apps\web\src\app\api\auth\[...all]\route.ts)
  ⎿  Added 41 lines, removed 1 line
       1  import { handler } from "@/lib/convex/server";
       2
       3 -export const { GET, POST } = handler;
       3 +// kitcn proxy doesn't export OPTIONS, so handle CORS preflight here for the
       4 +// sentinel WebView (http://localhost:1420). Same-origin browser requests
       5 +// pass through untouched so Set-Cookie isn't mangled by Headers re-iteration.
       6 +const CROSS_ORIGIN_ALLOWED = new Set(["http://localhost:1420"]);
       7 +
       8 +function corsHeadersFor(origin: string | null): Record<string, string> | null {
       9 +  if (!origin || !CROSS_ORIGIN_ALLOWED.has(origin)) return null;
      10 +  return {
      11 +    "Access-Control-Allow-Origin": origin,
      12 +    "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
      13 +    "Access-Control-Allow-Headers": "authorization, content-type",
      14 +    "Access-Control-Allow-Credentials": "true",
      15 +    "Access-Control-Max-Age": "86400",
      16 +    Vary: "Origin",
      17 +  };
      18 +}
      19 +
      20 +function withCorsIfNeeded(request: Request, response: Response): Response {
      21 +  const cors = corsHeadersFor(request.headers.get("origin"));
      22 +  if (!cors) return response;
      23 +  const headers = new Headers(response.headers);
      24 +  for (const [k, v] of Object.entries(cors)) headers.set(k, v);
      25 +  return new Response(response.body, {
      26 +    status: response.status,
      27 +    statusText: response.statusText,
      28 +    headers,
      29 +  });
      30 +}
      31 +
      32 +export async function OPTIONS(request: Request) {
      33 +  const cors = corsHeadersFor(request.headers.get("origin"));
      34 +  return new Response(null, { status: 204, headers: cors ?? {} });
      35 +}
      36 +
      37 +export async function GET(request: Request) {
      38 +  return withCorsIfNeeded(request, await handler.GET(request));
      39 +}
      40 +
      41 +export async function POST(request: Request) {
      42 +  return withCorsIfNeeded(request, await handler.POST(request));
      43 +}