{"record":{"id":"6a7d38ba87463391","repo":"signalapp/Signal-Server","slug":"identity-type-must-be","errorCode":null,"errorMessage":"identity type must be ","messagePattern":"identity type must be ","errorType":"validation","errorClass":"FieldValidationException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/grpc/validators/ServiceIdentifierIdentityTypeValidator.java","lineNumber":47,"sourceCode":"          default -> throw new IllegalArgumentException(\"unsupported value: \" + d.getName());\n        };\n      }\n    }\n\n    throw new IllegalArgumentException(\"value must be org.signal.chat.require.IdentityType\");\n  }\n\n  @Override\n  protected void validateMessageValue(final IdentityType extensionValue, final Message msg) throws FieldValidationException {\n    if (msg == null) {\n      return;\n    }\n\n    if (msg instanceof ServiceIdentifier i) {\n      if (i.getIdentityType() == extensionValue) {\n        return;\n      }\n      throw new FieldValidationException(\"identity type must be \" + extensionValue.getValueDescriptor().getName());\n    }\n\n    throw new IllegalArgumentException(\"field must be \" + ServiceIdentifier.class.getName());\n  }\n}\n","sourceCodeStart":29,"sourceCodeEnd":53,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/grpc/validators/ServiceIdentifierIdentityTypeValidator.java#L29-L53","documentation":"ServiceIdentifierIdentityTypeValidator.validateMessageValue throws FieldValidationException when a ServiceIdentifier message field carries an identity type (ACI/PNI) different from the one required by the annotation. The message ends with the expected identity type name from the proto enum descriptor.","triggerScenarios":"Passing a PNI service identifier where an ACI is required (or vice versa) in a Signal gRPC request field annotated with a specific IdentityType, e.g. sending a PNI:... identifier to an endpoint expecting ACI.","commonSituations":"Mixing up ACI and PNI UUIDs stored client-side; using a generic ServiceId where a typed one is required; refactors that swap identifier fields.","solutions":["Pass an identifier whose identity type matches the annotation (e.g. AciServiceIdentifier for ACI fields)","Parse identifiers with the correct typed valueOf (AciServiceIdentifier.valueOf vs PniServiceIdentifier.valueOf)","Check the prefix (\"ACI:\" vs \"PNI:\") of the identifier string before sending"],"exampleFix":"// before\nServiceIdentifier id = PniServiceIdentifier.valueOf(pniString); // field requires ACI\n// after\nServiceIdentifier id = AciServiceIdentifier.valueOf(aciString);","handlingStrategy":"type-guard","validationCode":"if (id instanceof PniServiceIdentifier && fieldRequiresAci) throw new IllegalArgumentException(\"expected ACI identifier\");","typeGuard":"boolean isAci(ServiceIdentifier id) { return id instanceof AciServiceIdentifier; }","tryCatchPattern":"try { stub.call(request); } catch (StatusRuntimeException e) { if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT && e.getMessage().startsWith(\"identity type must be\")) { /* handle wrong identity type */ } }","preventionTips":["Use AciServiceIdentifier/PniServiceIdentifier types instead of raw strings","Check the identifier's string prefix (ACI:/PNI:) before sending","Keep ACI and PNI values in separate, named fields/variables"],"tags":["grpc","validation","identity-type","service-identifier"],"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-17T15:17:12.973Z"}