{"record":{"id":"749c35b658c73e07","repo":"signalapp/Signal-Server","slug":"subscriberid-mismatch","errorCode":null,"errorMessage":"subscriberId mismatch","messagePattern":"subscriberId mismatch","errorType":"exception","errorClass":"SubscriptionForbiddenException","httpStatus":403,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/storage/SubscriptionManager.java","lineNumber":116,"sourceCode":"\n  /**\n   * Create or update a subscriber in the subscriptions table\n   * <p>\n   * If the subscriber does not exist, a subscriber with the provided credentials will be created. If the subscriber\n   * already exists, its last access time will be updated.\n   *\n   * @param subscriberCredentials Subscriber credentials derived from the subscriberId\n   * @param createPermitted Whether creating a new subscriber is permitted if one does not exist\n   * @throws SubscriptionForbiddenException if the subscriber credentials were incorrect\n   * @throws SubscriberIdCreationNotPermittedException if a new subscriber ID would be created, but the caller does not permit it\n   */\n  public void updateSubscriber(final SubscriberCredentials subscriberCredentials, final boolean createPermitted)\n      throws SubscriptionForbiddenException, SubscriberIdCreationNotPermittedException {\n    final Subscriptions.GetResult getResult =\n        subscriptions.get(subscriberCredentials.subscriberUser(), subscriberCredentials.hmac());\n\n    if (getResult == Subscriptions.GetResult.PASSWORD_MISMATCH) {\n      throw new SubscriptionForbiddenException(\"subscriberId mismatch\");\n    } else if (getResult == Subscriptions.GetResult.NOT_STORED) {\n\n      if (!createPermitted) {\n        throw new SubscriberIdCreationNotPermittedException();\n      }\n\n      // create a customer and write it to ddb\n      final Subscriptions.Record updatedRecord = subscriptions.create(subscriberCredentials.subscriberUser(),\n          subscriberCredentials.hmac(),\n          subscriberCredentials.now());\n      if (updatedRecord == null) {\n        throw new SubscriptionForbiddenException(\"subscriberId mismatch\");\n      }\n    } else {\n      // already exists so just touch access time and return\n      subscriptions.accessedAt(subscriberCredentials.subscriberUser(), subscriberCredentials.now());\n    }\n  }","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/storage/SubscriptionManager.java#L98-L134","documentation":"updateSubscriber throws SubscriptionForbiddenException(\"subscriberId mismatch\") when subscriptions.get() returns PASSWORD_MISMATCH: the subscriberUser exists but the presented HMAC does not match the stored one. It means the client is using credentials (username/HMAC pair) that do not correspond to the stored subscription record.","triggerScenarios":"Calling SubscriptionManager.updateSubscriber with SubscriberCredentials whose hmac() fails verification against the stored record for subscriberUser() (Subscriptions.GetResult.PASSWORD_MISMATCH).","commonSituations":"Client regenerated its subscriberId locally but kept an old stored one (or vice versa) after a reinstall; key material out of sync between client devices; a different client trying to update someone else's subscription id; truncated/corrupted subscriberId sent over the wire.","solutions":["Re-derive and resend the correct subscriberId/HMAC pair on the client, ensuring both are persisted atomically together.","If the subscriberId is lost, call updateSubscriber with createPermitted=true and a freshly generated subscriberUser/hmac to create a new record instead of guessing the old credentials.","Verify the client is not URL-encoding/base64-encoding the subscriberId differently than when it was created.","As a last resort, delete the DDB record for that subscriberUser so a fresh create can succeed."],"exampleFix":"// before\nmanager.updateSubscriber(SubscriberCredentials.process(oldSubscriberId, staleHmac, clock), true);\n// after\nbyte[] subscriberUser = generateNewSubscriberUser();\nbyte[] hmac = computeHmac(subscriberUser);\nmanager.updateSubscriber(SubscriberCredentials.process(subscriberUser, hmac, clock), true);","handlingStrategy":"validation","validationCode":"byte[] stored = keyStore.loadSubscriberHmac(subscriberUser);\nif (stored == null || !MessageDigest.isEqual(stored, hmac)) {\n  regenerateCredentials(); // before calling updateSubscriber\n}","typeGuard":null,"tryCatchPattern":"try {\n  manager.updateSubscriber(creds, createPermitted);\n} catch (SubscriptionForbiddenException e) {\n  log.warn(\"subscriberId HMAC mismatch — regenerate credentials and retry with createPermitted=true\");\n}","preventionTips":["Persist subscriberUser and HMAC key atomically on the client","Never regenerate credentials on retry — reuse the same pair","Use one canonical base64/URL encoding for subscriberId everywhere"],"tags":["subscriptions","hmac","auth","mismatch"],"backgroundTag":"authentication-failed","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"}