{"record":{"id":"a5ae4d6a7e400be8","repo":"toeverything/AFFiNE","slug":"invalid-checkout-parameters-a5ae4d","errorCode":"invalid_checkout_parameters","errorMessage":"Invalid checkout parameters provided.","messagePattern":"Invalid checkout parameters provided\\.","errorType":"exception","errorClass":"InvalidCheckoutParameters","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/plugins/payment/service.ts","lineNumber":144,"sourceCode":"    params: z.infer<typeof CheckoutParams>,\n    args: z.infer<typeof CheckoutExtraArgs>\n  ) {\n    const { plan, recurring, variant } = params;\n\n    if (\n      env.namespaces.canary &&\n      env.prod &&\n      args.user &&\n      !this.feature.isStaff(args.user.email)\n    ) {\n      throw new ActionForbidden();\n    }\n\n    const manager = this.select(plan);\n    const result = CheckoutExtraArgs.safeParse(args);\n\n    if (!result.success) {\n      throw new InvalidCheckoutParameters();\n    }\n\n    return manager.checkout(\n      {\n        plan,\n        recurring,\n        variant: variant ?? null,\n      },\n      params,\n      args\n    );\n  }\n\n  async cancelSubscription(\n    identity: z.infer<typeof SubscriptionIdentity>,\n    idempotencyKey?: string\n  ): Promise<Subscription> {\n    this.assertSubscriptionIdentity(identity);","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/payment/service.ts#L126-L162","documentation":"Thrown by SubscriptionService.checkout when CheckoutExtraArgs.safeParse(args) fails. CheckoutExtraArgs is a zod union of UserSubscriptionCheckoutArgs, WorkspaceSubscriptionCheckoutArgs, and SelfhostTeamCheckoutArgs, so the extra args must structurally match the plan's expected shape (e.g. team checkout needs workspace args, selfhost team needs license/seat args).","triggerScenarios":"Calling checkout with plan 'team' but user-shaped args (userId instead of workspaceId), or plan 'selfhostedteam' without the required selfhost fields; sending extra/misspelled keys so no union member matches; passing args as a JSON string instead of an object.","commonSituations":"One generic checkout form reused for all plans; frontend args schema drifted from the server's zod definitions after a backend update; missing idempotency/variant fields newly required.","solutions":["Mirror the server's CheckoutExtraArgs union in the client and zod-parse args before calling checkout; use safeParse to surface which union branch failed.","Match args shape to plan: team -> workspace checkout args, pro/ai -> user checkout args, selfhostedteam -> selfhost team args.","Regenerate/refresh client types from the backend schema after upgrading the payment plugin."],"exampleFix":"// before\nawait svc.checkout(\n  { plan: 'team', recurring: 'monthly' },\n  { userId: user.id } // wrong union branch for team\n);\n\n// after\nawait svc.checkout(\n  { plan: 'team', recurring: 'monthly' },\n  { workspaceId: workspace.id } // matches WorkspaceSubscriptionCheckoutArgs\n);","handlingStrategy":"validation","validationCode":"import { z } from 'zod';\nconst CheckoutArgs = z.union([\n  z.object({ userId: z.string() }),\n  z.object({ workspaceId: z.string() }),\n  z.object({ licenseId: z.string(), seats: z.number().int().min(1) }), // mirror selfhost args\n]);\nconst parsed = CheckoutArgs.safeParse(args);\nif (!parsed.success) throw new Error(parsed.error.issues.join('; '));\nawait svc.checkout({ plan, recurring, variant }, parsed.data);","typeGuard":"function matchesPlanArgs(plan: string, args: unknown): boolean {\n  if (plan === 'team') return !!args && typeof (args as any).workspaceId === 'string';\n  if (plan === 'selfhostedteam') return !!args && 'licenseId' in (args as object);\n  if (plan === 'pro' || plan === 'ai') return !!args && 'userId' in (args as object);\n  return false;\n}","tryCatchPattern":"catch (e) { if (gqlErrorCode(e) === 'invalid_checkout_parameters') { const r = CheckoutArgs.safeParse(args); highlightForm(r.error?.issues); return; } throw e; }","preventionTips":["Keep the client zod schema in lockstep with the server's CheckoutExtraArgs union.","Run safeParse client-side before every checkout call and surface issues on the form.","Add a contract test comparing client and server arg schemas on CI."],"tags":["payment","checkout","zod","validation","schema-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}