hcengineering/platform · error · PlatformError

Conflict

Conflict

Error message

Verifying new social id belonging to person w/o account but it's already verified

What it means

In validateOtp, while verifying a new email social id that belongs to a person who has no account yet, the social id turns out to already have verifiedOn set — which should be impossible at this point. The server treats it as an inconsistent state and throws Conflict.

Source

Thrown at server/account/src/operations.ts:484

      if (callerAccountUuid == null) {
        throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, {}))
      }

      if (targetAccount == null) {
        // only person exists means there's no verified social id associated with it -> merge it to the current account
        // doMergePersons will fail if there's a verified social id

        await doMergePersons(db, callerAccountUuid, emailSocialId.personUuid)

        // what happens to local persons referencing this person in various workspaces?
        // there can be some persons but no Employees because there's no account
        // we know where the person is migrated to so can update later as needed

        if (emailSocialId.verifiedOn == null) {
          await db.socialId.update({ _id: emailSocialId._id }, { verifiedOn: Date.now() })
        } else {
          // Normally, it should not be the case
          ctx.warn("Verifying new social id belonging to person w/o account but it's already verified", {
            emailSocialId,
            callerAccountUuid
          })
          throw new PlatformError(new Status(Severity.ERROR, platform.status.Conflict, {}))
        }
      } else {
        if (callerAccountUuid === targetAccount.uuid) {
          if (emailSocialId.verifiedOn == null) {
            await db.socialId.update({ _id: emailSocialId._id }, { verifiedOn: Date.now() })
          }
        } else {
          if (emailSocialId.verifiedOn == null) {
            // Move the target social id to current account, we can easily do this because it was not verified
            await db.socialId.update(
              { _id: emailSocialId._id },
              { personUuid: callerAccountUuid, verifiedOn: Date.now() }
            )
          } else {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Avoid duplicate validateOtp calls for the same social id — make the request idempotent client-side.
  2. If hit after a migration, correct the data: either clear verifiedOn or link an account to the person, so state matches the expected invariant.
  3. Retry the signup flow from the beginning to generate a fresh social id/OTP.
  4. Check for concurrent sessions validating the same email and serialize the flow.

Example fix

// before
await client.validateOtp(otp) // may throw Conflict on replay
// after
if (!otpValidationPending) return // already validated
try {
  await client.validateOtp(otp)
  otpValidationPending = false
} catch (e) {
  if (isStatusError(e, platform.status.Conflict)) refreshSignupState()
  else throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

function isConflictStatus(e: unknown): boolean {
  return e instanceof PlatformError && e.status.code === platform.status.Conflict
}

Try / catch

try {
  await client.validateOtp(otp)
} catch (e) {
  if (isConflictStatus(e)) {
    // already verified: treat as success or restart signup flow
    restartSignupFlow()
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Calling validateOtp with a callerAccountUuid whose person has an email social id that was already verified (verifiedOn != null) through a prior OTP validation or a migration that pre-verified ids.

Common situations: Replaying/resharing the same OTP validation request; concurrent OTP validation from two sessions; data migrations that set verifiedOn on social ids for account-less persons, breaking the assumption in this code path.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/661ed65d3f88dc7d. Report an issue: GitHub.