{"record":{"id":"36cb5c3d5797334d","repo":"signalapp/Signal-Server","slug":"pni-account-identifier-did-not-start-with-pni-p","errorCode":null,"errorMessage":"PNI account identifier did not start with \"PNI:\" prefix","messagePattern":"PNI account identifier did not start with \"PNI:\" prefix","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/identity/PniServiceIdentifier.java","lineNumber":63,"sourceCode":"  @Override\n  public byte[] toFixedWidthByteArray() {\n    final ByteBuffer byteBuffer = ByteBuffer.allocate(17);\n    byteBuffer.put(IDENTITY_TYPE.getBytePrefix());\n    byteBuffer.putLong(uuid.getMostSignificantBits());\n    byteBuffer.putLong(uuid.getLeastSignificantBits());\n    byteBuffer.flip();\n\n    return byteBuffer.array();\n  }\n\n  @Override\n  public ServiceId.Pni toLibsignal() {\n    return new ServiceId.Pni(uuid);\n  }\n\n  public static PniServiceIdentifier valueOf(final String string) {\n    if (!string.startsWith(IDENTITY_TYPE.getStringPrefix())) {\n      throw new IllegalArgumentException(\"PNI account identifier did not start with \\\"PNI:\\\" prefix\");\n    }\n\n    return new PniServiceIdentifier(UUID.fromString(string.substring(IDENTITY_TYPE.getStringPrefix().length())));\n  }\n\n  public static PniServiceIdentifier fromBytes(final byte[] bytes) {\n    if (bytes.length == 17) {\n      if (bytes[0] != IDENTITY_TYPE.getBytePrefix()) {\n        throw new IllegalArgumentException(\"Unexpected byte array prefix: \" + HexFormat.of().formatHex(new byte[] { bytes[0] }));\n      }\n\n      return new PniServiceIdentifier(UUIDUtil.fromBytes(Arrays.copyOfRange(bytes, 1, bytes.length)));\n    }\n\n    throw new IllegalArgumentException(\"Unexpected byte array length: \" + bytes.length);\n  }\n}\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/identity/PniServiceIdentifier.java#L45-L81","documentation":"PniServiceIdentifier.valueOf parses a \"PNI:\"-prefixed string into a PniServiceIdentifier and throws IllegalArgumentException if the string lacks the required \"PNI:\" prefix. This is Signal's guard ensuring PNI account identifiers carry their identity-type prefix before UUID parsing.","triggerScenarios":"Calling PniServiceIdentifier.valueOf() with a raw UUID string, an ACI-prefixed string (\"ACI:...\"), a bare hex UUID, or a string with wrong casing/whitespace before the prefix.","commonSituations":"Storing identifiers without their prefix in databases and re-parsing later; mixing ACI and PNI strings; user/config input containing unprefixed UUIDs; older persisted formats predating prefixes.","solutions":["Normalize the string to include the \"PNI:\" prefix before parsing (e.g. if (!s.startsWith(\"PNI:\")) s = \"PNI:\" + s)","Parse raw UUIDs with new PniServiceIdentifier(UUID.fromString(...)) instead of valueOf","Verify you are not accidentally passing an ACI identifier; use AciServiceIdentifier for ACI values"],"exampleFix":"// before\nPniServiceIdentifier pni = PniServiceIdentifier.valueOf(uuidString); // no PNI: prefix\n// after\nString prefixed = uuidString.startsWith(\"PNI:\") ? uuidString : \"PNI:\" + uuidString;\nPniServiceIdentifier pni = PniServiceIdentifier.valueOf(prefixed);","handlingStrategy":"try-catch","validationCode":"if (s == null || !s.startsWith(\"PNI:\")) throw new IllegalArgumentException(\"PNI identifier must start with PNI:\");","typeGuard":"boolean isPniString(String s) { return s != null && s.startsWith(\"PNI:\"); }","tryCatchPattern":"try { pni = PniServiceIdentifier.valueOf(s); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"PNI:\")) { /* handle missing prefix / wrong identifier type */ } }","preventionTips":["Always persist service identifiers with their ACI:/PNI: prefix","Route parsing through typed valueOf methods matching the prefix","Fail fast on identifier format at ingestion boundaries"],"tags":["parsing","identifier","uuid","pni"],"backgroundTag":"invalid-identifier-format","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}