{"record":{"id":"2a6a80763c647a8f","repo":"hcengineering/platform","slug":"badrequest","errorCode":"BadRequest","errorMessage":"platform.status.BadRequest","messagePattern":"platform\\.status\\.BadRequest","errorType":"error_code","errorClass":"PlatformError","httpStatus":null,"severity":"error","filePath":"server/account/src/operations.ts","lineNumber":1581,"sourceCode":" * email+password as a secondary sign-in method.\n *\n * Requires authentication (session token). Only valid for accounts that have\n * no password set — accounts with an existing password must use changePassword.\n */\nexport async function requestPasswordSetup (\n  ctx: MeasureContext,\n  db: AccountDB,\n  branding: Branding | null,\n  token: string\n): Promise<void> {\n  const { account: accountUuid } = decodeTokenVerbose(ctx, token)\n\n  // Guard: reject if the account already has a password. The setup flow\n  // bypasses the old-password requirement in changePassword, so it must only\n  // be accessible to accounts that have no password yet.\n  const existingAccount = await getAccount(db, accountUuid)\n  if (existingAccount?.hash != null && existingAccount?.salt != null) {\n    throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))\n  }\n\n  ctx.info('Requesting password setup', { accountUuid })\n\n  const emailSocialId = await db.socialId.findOne({\n    type: SocialIdType.EMAIL,\n    personUuid: accountUuid\n  })\n\n  if (emailSocialId == null) {\n    ctx.error('Email social id not found for account', { accountUuid })\n    throw new PlatformError(\n      new Status(Severity.ERROR, platform.status.SocialIdNotFound, { value: '', type: SocialIdType.EMAIL })\n    )\n  }\n\n  const { mailURL, mailAuth } = getMailUrl()\n  const front = getFrontUrl(branding)","sourceCodeStart":1563,"sourceCodeEnd":1599,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/server/account/src/operations.ts#L1563-L1599","documentation":"Thrown by the password-setup (requestPasswordSetup) operation when the target account already has a password hash and salt. The setup flow is only for accounts that never had a password (e.g. signup-via-email accounts), because it bypasses the old-password check in changePassword.","triggerScenarios":"Calling the password-setup request endpoint for an accountUuid whose existingAccount.hash != null && salt != null.","commonSituations":"User already completed setup and clicks the setup link again; stale or reused setup links; client mistakenly routing a password-reset through the setup endpoint; duplicated signup.","solutions":["Route the user to the normal password-reset (restore) flow instead of password setup.","Use changePassword with the existing old password when the account already has credentials.","Invalidate stale setup links/tokens in emails.","Check account.hash/salt client-side if account info is available before requesting setup."],"exampleFix":"// before: always call setup flow\nawait accountClient.requestPasswordSetup(accountUuid)\n// after: only when no password set yet\nif (existingAccount?.hash == null && existingAccount?.salt == null) {\n  await accountClient.requestPasswordSetup(accountUuid)\n} else {\n  await accountClient.requestRestore(ctx, email)\n}","handlingStrategy":"validation","validationCode":"const acct = await getAccount(db, accountUuid)\nconst needsSetup = acct?.hash == null && acct?.salt == null\nif (!needsSetup) throw new Error('Account already has a password; use restore flow')","typeGuard":"function hasNoPassword(a: { hash?: string | null; salt?: string | null }): boolean { return a.hash == null && a.salt == null }","tryCatchPattern":"try {\n  await accountClient.requestPasswordSetup(ctx, branding, accountUuid)\n} catch (err) {\n  if (err instanceof PlatformError && err.status.code === platform.status.BadRequest) {\n    await accountClient.requestRestore(ctx, email) // fall back to reset flow\n  } else throw err\n}","preventionTips":["Expire setup links after first use.","Detect password-exists state in the UI and hide the setup link.","Never reuse setup endpoints for password resets."],"tags":["account","password","validation"],"backgroundTag":"account-already-has-password","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}