{"record":{"id":"2cc82ba786d88fc6","repo":"signalapp/Signal-Server","slug":"400-bad-request","errorCode":null,"errorMessage":"400 Bad Request","messagePattern":"400 Bad Request","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java","lineNumber":484,"sourceCode":"\n      Metrics.counter(CAPTCHA_ATTEMPT_COUNTER_NAME, Tags.of(\n              Tag.of(SUCCESS_TAG_NAME, String.valueOf(assessmentResult.isValid(captchaScoreThreshold))),\n              UserAgentTagUtil.getPlatformTag(userAgent),\n              Tag.of(COUNTRY_CODE_TAG_NAME, Util.getCountryCode(registrationServiceSession.number())),\n              Tag.of(REGION_CODE_TAG_NAME, Util.getRegion(registrationServiceSession.number())),\n              Tag.of(SCORE_TAG_NAME, assessmentResult.getScoreString())))\n          .increment();\n\n      CaptchaMetrics.measureCaptchaOutcome(assessmentResult.getNormalizedIntScore(),\n          assessmentResult.isValid(captchaScoreThreshold),\n          Util.getRegion(registrationServiceSession.number()),\n          \"verification\");\n\n    } catch (final IOException e) {\n      logger.error(\"error assessing captcha during registration verification\", e);\n      throw new ServerErrorException(Response.Status.SERVICE_UNAVAILABLE, e);\n    } catch (InvalidCaptchaArgumentException e) {\n      throw new BadRequestException(e);\n    }\n\n    if (assessmentResult.isValid(captchaScoreThreshold)) {\n      final List<VerificationSession.Information> submittedInformation = new ArrayList<>(\n          verificationSession.submittedInformation());\n      submittedInformation.add(VerificationSession.Information.CAPTCHA);\n\n      final List<VerificationSession.Information> requestedInformation = new ArrayList<>(\n          verificationSession.requestedInformation());\n      // a captcha satisfies a push challenge, in case of push deliverability issues\n      requestedInformation.remove(VerificationSession.Information.PUSH_CHALLENGE);\n      final boolean allowedToRequestCode = (verificationSession.allowedToRequestCode()\n          || requestedInformation.remove(VerificationSession.Information.CAPTCHA))\n          && requestedInformation.isEmpty();\n\n      verificationSession = new VerificationSession(verificationSession.sessionId(),\n          verificationSession.pushChallenge(),\n          verificationSession.carrierData(),","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java#L466-L502","documentation":"handleCaptcha throws BadRequestException (HTTP 400) when captcha assessment fails with InvalidCaptchaArgumentException, meaning the captcha argument supplied by the client is malformed or unusable — e.g. missing, empty, or structurally invalid token rather than merely a low-scored one. The server rejects the request without consuming a verification attempt.","triggerScenarios":"updateSession called with a captcha field that is null/empty/blank or otherwise fails the captcha client's argument validation before any remote assessment (InvalidCaptchaArgumentException from the captcha client).","commonSituations":"Client sending an empty captcha header when no captcha was actually solved; truncating a long captcha token; sending the token in the wrong field/header; frontend SDK failing silently and returning an empty token.","solutions":["Ensure the captcha token is present and non-empty before calling updateSession; don't send the captcha field unless a token was obtained.","Fetch a fresh token from the captcha widget/SDK and pass it verbatim (no trimming/truncation).","Check you are using the captcha endpoint matching the service's configured site key so the token shape is accepted.","Inspect the wrapped InvalidCaptchaArgumentException message for the exact argument problem."],"exampleFix":"// before\nupdateSession(captcha: token ?? \"\")\n// after\nif (token != null && !token.isEmpty()) { updateSession(captcha: token); }","handlingStrategy":"validation","validationCode":"if (typeof token !== 'string' || token.length === 0) { throw new Error('captcha token required'); }","typeGuard":"function hasCaptchaToken(args) { return typeof args.captcha === 'string' && args.captcha.length > 0; }","tryCatchPattern":"try { await updateSession(...); } catch (e) {\n  if (e.status === 400) { const t = await fetchNewCaptchaToken(); return updateSession({captcha: t}); }\n  throw e;\n}","preventionTips":["Never send an empty captcha field","Pass the token verbatim without truncation","Only include the captcha field when a token was actually obtained"],"tags":["http-400","bad-request","captcha","validation"],"backgroundTag":"invalid-argument-value","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"}