{"record":{"id":"94c31c57f9207602","repo":"toeverything/AFFiNE","slug":"invalid-auth-state-94c31c","errorCode":"invalid_auth_state","errorMessage":"Invalid auth state. You might start the auth progress from another device.","messagePattern":"Invalid auth state\\. You might start the auth progress from another device\\.","errorType":"exception","errorClass":"InvalidAuthState","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/core/auth/controller.ts","lineNumber":232,"sourceCode":"  @Public()\n  @UseNamedGuard('version')\n  @Post('/open-app/sign-in-code')\n  async openAppSignInCode(@CurrentUser() user?: CurrentUser) {\n    if (!user) throw new ActionForbidden();\n    const code = await this.openApp.createSignInCode(user);\n    return { code };\n  }\n\n  @Public()\n  @UseNamedGuard('version')\n  @Post('/open-app/sign-in')\n  async openAppSignIn(\n    @Req() req: Request,\n    @Res() res: Response,\n    @Body() body?: unknown\n  ) {\n    const credential = OpenAppSignInBodySchema.safeParse(body);\n    if (!credential.success) throw new InvalidAuthState();\n    const identity = await this.openApp.verifySignInCode(credential.data.code);\n    const { exchangeCode } = await this.sessionIssuer.issue(req, res, identity);\n    res.send({ id: identity.userId, exchangeCode });\n  }\n\n  @Public()\n  @UseNamedGuard('version')\n  @Post('/session/exchange')\n  @Header('Cache-Control', 'no-store')\n  @Header('Pragma', 'no-cache')\n  async exchangeSession(@Req() req: Request, @Body() body?: unknown) {\n    const input = AuthSessionExchangeBodySchema.parse(body);\n    return await this.sessionExchange.exchange(req, input.code, {\n      installationId: input.installationId,\n      platform: input.platform,\n      deviceName: input.deviceName,\n      appVersion: getClientVersionFromRequest(req) ?? undefined,\n    });","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/auth/controller.ts#L214-L250","documentation":"Thrown by `POST /api/auth/open-app/sign-in` when the body fails `OpenAppSignInBodySchema.safeParse`. The schema is `{ code: z.string().min(1).max(512) }.strict()`, so a missing/empty/too-long `code`, an extra field, or a non-object body all fail. HTTP 400 (presented as invalid_auth_state because the one-time code is the auth state).","triggerScenarios":"Posting `{}` or `{ code: '' }`, omitting the code from the desktop handoff, replaying a code format the strict schema rejects, or attaching extra fields.","commonSituations":"Desktop client sends an empty code before the browser generated one, the code query param was lost in the deep-link handoff, or a client version sends a different payload shape.","solutions":["Send exactly `{ code: string }` with a 1–512 char code obtained from `/open-app/sign-in-code`.","Validate the code is non-empty on the client before posting.","Regenerate the code if it was already consumed or expired (codes live 60s)."],"exampleFix":"// before\nfetch('/api/auth/open-app/sign-in', { method: 'POST', body: '{}' });\n\n// after\nfetch('/api/auth/open-app/sign-in', {\n  method: 'POST',\n  headers: { 'content-type': 'application/json' },\n  body: JSON.stringify({ code }),\n});","handlingStrategy":"validation","validationCode":"import { z } from 'zod';\nconst Body = z.object({ code: z.string().min(1).max(512) }).strict();\nconst parsed = Body.safeParse(payload);\nif (!parsed.success) throw new Error('code required (1-512 chars)');\nawait fetch('/api/auth/open-app/sign-in', {\n  method: 'POST',\n  headers: { 'content-type': 'application/json' },\n  body: JSON.stringify(parsed.data),\n});","typeGuard":"function isOpenAppSignInBody(v: unknown): v is { code: string } {\n  return typeof v === 'object' && v !== null &&\n    typeof (v as any).code === 'string' &&\n    (v as any).code.length >= 1 && (v as any).code.length <= 512;\n}","tryCatchPattern":null,"preventionTips":["Validate the code is non-empty and ≤512 chars before posting.","Regenerate the code if older than 60 seconds (challenge TTL).","Match the strict schema — send only `{ code }`."],"tags":["validation","open-app","sign-in","input"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}