{"record":{"id":"d327cf6bdd6872ba","repo":"signalapp/Signal-Server","slug":"return-response-status-404-build","errorCode":null,"errorMessage":"return Response.status(404).build();","messagePattern":"return Response\\.status\\(404\\)\\.build\\(\\);","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/controllers/ChallengeController.java","lineNumber":199,"sourceCode":"      description = \"If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed\"))\n  @ApiResponse(responseCode = \"429\", description = \"Too many attempts\", headers = @Header(\n      name = \"Retry-After\",\n      description = \"If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed\"))\n  public Response requestPushChallenge(@Auth final AuthenticatedDevice auth,\n      @Context ContainerRequestContext requestContext) {\n\n    final Account account = accountsManager.getByAccountIdentifier(auth.accountIdentifier())\n        .orElseThrow(() -> new WebApplicationException(Response.Status.UNAUTHORIZED));\n\n    final ChallengeConstraints constraints = challengeConstraintChecker.challengeConstraintsHttp(requestContext, account);\n    if (!constraints.pushPermitted()) {\n      return Response.status(429).build();\n    }\n    try {\n      rateLimitChallengeManager.sendPushChallenge(account);\n      return Response.status(200).build();\n    } catch (final NotPushRegisteredException e) {\n      return Response.status(404).build();\n    }\n  }\n}\n","sourceCodeStart":181,"sourceCodeEnd":203,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ChallengeController.java#L181-L203","documentation":"The push-challenge endpoint returns HTTP 404 when rateLimitChallengeManager.sendPushChallenge(account) throws NotPushRegisteredException, meaning none of the account's devices currently holds a valid push token/channel. The server cannot deliver the push challenge because there is no registered push destination for the account.","triggerScenarios":"Calling POST /v1/challenge/push for an account whose devices have no active APNs/FCM registration (unregistered or stale push credentials).","commonSituations":"Client reinstalled the app or revoked notification permissions so the push token was never re-registered; stale device entries after account recovery; testing against accounts that never registered with push providers.","solutions":["Re-register the device's push token (APNs/FCM) so the account has a push channel","Remove stale devices from the account via linked-device management","Verify the client completed device provisioning/registration before requesting push challenges","Check server-side push credential configuration (APNs keys, FCM sender) if all accounts 404"],"exampleFix":"// before\nchallengeClient.requestPushChallenge();\n// after\ntry {\n  challengeClient.requestPushChallenge();\n} catch (HttpURLConnection404) {\n  pushTokenManager.registerNewToken(); // re-establish push channel, then retry\n}","handlingStrategy":"validation","validationCode":"// ensure a push token exists before requesting a push challenge\nif (!pushTokenRegistry.hasValidToken(account)) {\n  pushTokenRegistry.registerToken(); // FCM/APNs\n}\nchallengeClient.requestPushChallenge();","typeGuard":"boolean hasPushChannel(Account a) {\n  return a.getDevices().stream().anyMatch(d -> d.getPushToken() != null && !d.getPushToken().isBlank());\n}","tryCatchPattern":"try { challengeClient.requestPushChallenge(); }\ncatch (NotFoundException e) {\n  reRegisterPushToken();\n  retryOnce();\n}","preventionTips":["Register push tokens immediately after app install/reinstall","Prune unlinked/stale devices from accounts","Verify push provider credentials on the server","Re-provision after notification permission changes"],"tags":["http","push-notifications","jaxrs","device-registration"],"backgroundTag":"resource-not-found","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"}