{"record":{"id":"726ebb5294eca488","repo":"signalapp/Signal-Server","slug":"repeated-field-is-expected-to-be-non-empty","errorCode":null,"errorMessage":"repeated field is expected to be non-empty","messagePattern":"repeated field is expected to be non-empty","errorType":"validation","errorClass":"FieldValidationException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/grpc/validators/NonEmptyFieldValidator.java","lineNumber":56,"sourceCode":"  @Override\n  protected void validateStringValue(\n      final Boolean extensionValue,\n      final String fieldValue) throws FieldValidationException {\n    if (StringUtils.isNotEmpty(fieldValue)) {\n      return;\n    }\n    throw new FieldValidationException(\"string expected to be non-empty\");\n  }\n\n  @Override\n  protected void validateRepeatedField(\n      final Boolean extensionValue,\n      final Descriptors.FieldDescriptor fd,\n      final List<?> repeated) throws FieldValidationException {\n    if (repeated.size() > 0) {\n      return;\n    }\n    throw new FieldValidationException(\"repeated field is expected to be non-empty\");\n  }\n}\n","sourceCodeStart":38,"sourceCodeEnd":59,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/grpc/validators/NonEmptyFieldValidator.java#L38-L59","documentation":"NonEmptyFieldValidator.validateRepeatedField throws FieldValidationException when a repeated proto field marked required-non-empty has zero elements. It ensures list-type fields passed to Signal gRPC endpoints actually contain at least one item.","triggerScenarios":"Sending a request to a Signal gRPC method where a required repeated field (e.g. a list of identifiers, devices, or messages) is empty or unset.","commonSituations":"Batch APIs called with nothing to process; clients building requests in loops that processed zero items; filters that removed all elements before the request was built.","solutions":["Populate at least one element in the repeated field before building the request","Guard client-side: bail out early if the list is empty instead of issuing the call","If empty batches are legitimate, remove the non-empty annotation server-side"],"exampleFix":"// before\nif (ids.isEmpty()) { /* still sends request */ }\ngrpcStub.update(requestBuilder.addAllIds(ids).build());\n// after\nif (!ids.isEmpty()) {\n  grpcStub.update(requestBuilder.addAllIds(ids).build());\n}","handlingStrategy":"validation","validationCode":"if (list == null || list.isEmpty()) throw new IllegalArgumentException(\"repeated field must contain at least one element\");","typeGuard":"boolean isNonEmptyList(List<?> l) { return l != null && !l.isEmpty(); }","tryCatchPattern":"try { stub.call(request); } catch (StatusRuntimeException e) { if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT) { /* handle empty list */ } }","preventionTips":["Skip the RPC entirely when a batch list is empty","Assert non-empty lists in request-builder helper methods","Test batch APIs with zero-item inputs"],"tags":["grpc","validation","protobuf"],"backgroundTag":"empty-required-field","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"}