{"record":{"id":"0745eed0878f0910","repo":"signalapp/Signal-Server","slug":"409-conflict-session-already-verified","errorCode":null,"errorMessage":"409 Conflict (session already verified)","messagePattern":"409 Conflict \\(session already verified\\)","errorType":"http","errorClass":"ClientErrorException","httpStatus":409,"severity":"warning","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java","lineNumber":597,"sourceCode":"      final Optional<String> acceptLanguage,\n      @NotNull @Valid final VerificationCodeRequest verificationCodeRequest,\n      @Context final ContainerRequestContext requestContext) throws Throwable {\n\n    final RegistrationServiceSession registrationServiceSession = retrieveRegistrationServiceSession(encodedSessionId);\n\n    final VerificationSession verificationSession;\n    {\n      final VerificationSession storedVerificationSession = retrieveVerificationSession(registrationServiceSession);\n\n      verificationSession =\n          registrationFraudChecker.checkSendVerificationCodeAttempt(requestContext, storedVerificationSession,\n                  registrationServiceSession.number())\n              .updatedSession()\n              .orElse(storedVerificationSession);\n    }\n\n    if (registrationServiceSession.verified()) {\n      throw new ClientErrorException(\n          Response.status(Response.Status.CONFLICT)\n              .entity(buildResponse(registrationServiceSession, verificationSession))\n              .build());\n    }\n\n    if (!verificationSession.allowedToRequestCode()) {\n      final Response.Status status = verificationSession.requestedInformation().isEmpty()\n          ? Response.Status.TOO_MANY_REQUESTS\n          : Response.Status.CONFLICT;\n\n      throw new ClientErrorException(\n          Response.status(status)\n              .entity(buildResponse(registrationServiceSession, verificationSession))\n              .build());\n    }\n\n    final MessageTransport messageTransport = verificationCodeRequest.transport().toMessageTransport();\n","sourceCodeStart":579,"sourceCodeEnd":615,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java#L579-L615","documentation":"requestVerificationCode throws this ClientErrorException with HTTP 409 CONFLICT when registrationServiceSession.verified() is true — the phone number has already been verified and a code cannot be requested again for that session. The response body includes the session state so the client can detect the already-verified condition and finish registration.","triggerScenarios":"POST to request a verification code for a session whose registration service session is already marked verified — e.g. calling requestVerificationCode again after a successful verification/registration check.","commonSituations":"Client retry logic re-sending a code request after verification already succeeded; double-submit from a UI; re-running an idempotency-unaware script; session reuse after the number was registered on another device.","solutions":["Treat the 409 as success: the number is already verified — proceed to the next registration step instead of requesting a code.","Inspect the response body session JSON (verified flag) and update your client state machine to stop code requests once verified.","If a new verification is genuinely needed, create a new verification session for the number.","Guard client retry logic: don't re-call requestVerificationCode after a prior flow reported verification success."],"exampleFix":"// before\nawait requestVerificationCode(sessionId, transport);\n// after\nconst resp = await requestVerificationCode(sessionId, transport);\nif (resp.status === 409) { proceedAsVerified(); return; }","handlingStrategy":"type-guard","validationCode":"const s = await getSession(sessionId);\nif (s.verified) { proceedAsVerified(); return; }","typeGuard":"function isSessionVerified(s) { return s && s.verified === true; }","tryCatchPattern":"try { await requestVerificationCode(...); } catch (e) {\n  if (e.status === 409 && e.body?.session?.verified) { proceedAsVerified(); return; }\n  throw e;\n}","preventionTips":["Check session.verified before requesting a code","Treat 409 as a success path in your state machine","Avoid duplicate/double-submitted requests"],"tags":["http-409","conflict","already-verified","verification"],"backgroundTag":"invalid-state-transition","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}