{"record":{"id":"70a07f64db7ffec8","repo":"signalapp/Signal-Server","slug":"bj","errorCode":null,"errorMessage":"bj","messagePattern":"bj","errorType":"validation","errorClass":"ObsoletePhoneNumberFormatException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/util/Util.java","lineNumber":239,"sourceCode":"   * @param phoneNumber the phone number to check.\n   * @return whether the provided phone number is an old-format Benin phone number\n   */\n  public static boolean isOldFormatBeninPhoneNumber(final Phonenumber.PhoneNumber phoneNumber) {\n    return \"BJ\".equals(PHONE_NUMBER_UTIL.getRegionCodeForNumber(phoneNumber)) &&\n        PHONE_NUMBER_UTIL.getNationalSignificantNumber(phoneNumber).length() == 8;\n  }\n\n  /**\n   * If applicable, return the canonical form of the provided phone number.\n   * This is relevant in cases where a numbering authority has changed the numbering format for a region.\n   *\n   * @param phoneNumber the phone number to canonicalize.\n   * @return the canonical phone number if applicable, otherwise the original phone number.\n   */\n  public static Phonenumber.PhoneNumber canonicalizePhoneNumber(final Phonenumber.PhoneNumber phoneNumber)\n      throws NumberParseException, ObsoletePhoneNumberFormatException {\n    if (isOldFormatBeninPhoneNumber(phoneNumber)) {\n      throw new ObsoletePhoneNumberFormatException(\"bj\");\n    }\n    return phoneNumber;\n  }\n\n  public static byte[] truncate(byte[] element, int length) {\n    byte[] result = new byte[length];\n    System.arraycopy(element, 0, result, 0, result.length);\n\n    return result;\n  }\n\n  public static void sleep(long i) {\n    try {\n      Thread.sleep(i);\n    } catch (final InterruptedException ignored) {\n    }\n  }\n","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/util/Util.java#L221-L257","documentation":"canonicalizePhoneNumber throws ObsoletePhoneNumberFormatException with message \"bj\" when the given number matches Signal's old Benin phone number format. Benin changed its numbering plan (old 8-digit numbers were prefixed), so numbers in the legacy format are deliberately rejected instead of being silently canonicalized. Callers must migrate the number to the new format or reject the account identifier.","triggerScenarios":"Calling Util.canonicalizePhoneNumber with a Phonenumber.PhoneNumber whose national number matches the legacy Benin (country code 229) pattern detected by isOldFormatBeninPhoneNumber, e.g. an 8-digit Benin subscriber number from before the 2024 numbering change.","commonSituations":"Long-lived accounts registered with old Benin numbers; data imported from old backups; clients that never re-registered after Benin's numbering plan change; bulk migration scripts processing pre-change contact lists.","solutions":["Reformat the number to Benin's new numbering plan (prefix the old national number per the regulator's migration rule, e.g. prepend the mandated digit(s)) before canonicalizing.","Have the user re-register/update their phone number to the new format.","Catch ObsoletePhoneNumberFormatException and treat the identifier as obsolete: prompt for re-verification rather than sending to the old number.","Update stored records via a migration that rewrites legacy Benin numbers to the new format.","Exclude obsolete-format numbers from bulk operations and log them for remediation."],"exampleFix":"// before\nPhonenumber.PhoneNumber n = phoneUtil.parse(\"+229 90 12 34 56\", null);\nPhonenumber.PhoneNumber canonical = Util.canonicalizePhoneNumber(n); // throws\n// after\nPhonenumber.PhoneNumber n = phoneUtil.parse(\"+229 0190 12 34 56\", null);\nPhonenumber.PhoneNumber canonical = Util.canonicalizePhoneNumber(n); // new numbering plan","handlingStrategy":"validation","validationCode":"// before canonicalizing\nPhonenumber.PhoneNumber n = phoneUtil.parse(raw, null);\nif (n.getCountryCode() == 229 && String.valueOf(n.getNationalNumber()).length() == 8) {\n    // legacy Benin format: apply the regulator's migration prefix first\n    n = new Phonenumber.PhoneNumber().setCountryCode(229)\n        .setNationalNumber(Long.parseLong(\"01\" + n.getNationalNumber()));\n}\nPhonenumber.PhoneNumber canonical = Util.canonicalizePhoneNumber(n);","typeGuard":null,"tryCatchPattern":"try {\n    canonical = Util.canonicalizePhoneNumber(number);\n} catch (ObsoletePhoneNumberFormatException e) {\n    // \"bj\": legacy Benin numbering plan\n    markNumberObsolete(number);\n    throw new WebApplicationException(Status.BAD_REQUEST);\n}","preventionTips":["Track national numbering-plan changes (Benin 2024 prefix migration) in a data migration","Re-verify Benin accounts registered before the numbering change","Never silently rewrite phone numbers; route obsolete formats to an explicit migration path","Log ObsoletePhoneNumberFormatException occurrences to find un-migrated records"],"tags":["phone-number","benin","obsolete-format","libphonenumber"],"backgroundTag":"deprecated-api-usage","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"}