{"record":{"id":"01185147aa7144e8","repo":"signalapp/Signal-Server","slug":"field-value-is-d-but-expected-to-be-within-the","errorCode":null,"errorMessage":"field value is [%d] but expected to be within the [%d, %d] range","messagePattern":"field value is \\[(.+?)\\] but expected to be within the \\[(.+?), (.+?)\\] range","errorType":"validation","errorClass":"FieldValidationException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/grpc/validators/RangeFieldValidator.java","lineNumber":54,"sourceCode":"  @Override\n  protected Range resolveExtensionValue(final Object extensionValue) {\n    final ValueRangeConstraint rangeConstraint = (ValueRangeConstraint) extensionValue;\n    final long min = rangeConstraint.hasMin() ? rangeConstraint.getMin() : Long.MIN_VALUE;\n    final long max = rangeConstraint.hasMax() ? rangeConstraint.getMax() : Long.MAX_VALUE;\n    return new Range(min, max);\n  }\n\n  @Override\n  protected void validateIntegerNumber(\n      final Range range,\n      final long fieldValue,\n      final Descriptors.FieldDescriptor.Type type) throws FieldValidationException {\n    if (fieldValue < 0 && UNSIGNED_TYPES.contains(type)) {\n      throw new FieldValidationException(\"field value is expected to be within the [%d, %d] range\".formatted(\n          range.min(), range.max()));\n    }\n    if (fieldValue < range.min() || fieldValue > range.max()) {\n      throw new FieldValidationException(\"field value is [%d] but expected to be within the [%d, %d] range\".formatted(\n          fieldValue, range.min(), range.max()));\n    }\n  }\n}\n","sourceCodeStart":36,"sourceCodeEnd":59,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/grpc/validators/RangeFieldValidator.java#L36-L59","documentation":"RangeFieldValidator.validateIntegerNumber throws FieldValidationException when an integer field value falls outside the annotated [min, max] range for the field. The message includes the actual value and the allowed range.","triggerScenarios":"Sending a Signal gRPC request with an integer field (e.g. device ID, registration ID, count) whose value exceeds or falls below the range annotation, such as deviceId 0 or registrationId outside 0..16777215.","commonSituations":"Off-by-one usage of IDs starting at 1 vs 0; clients generating random values without bounds; version skew where server-side range annotations tightened.","solutions":["Clamp or validate the integer against the documented range before sending","Generate IDs per the protocol spec's valid ranges","Read the server's range annotation for the field and enforce it in client validation"],"exampleFix":"// before\nint regId = new Random().nextInt();\nbuilder.setRegistrationId(regId);\n// after\nint regId = new SecureRandom().nextInt(16777216); // 0..16777215\nbuilder.setRegistrationId(regId);","handlingStrategy":"validation","validationCode":"if (value < MIN || value > MAX) throw new IllegalArgumentException(\"value \" + value + \" outside [\" + MIN + \", \" + MAX + \"]\");","typeGuard":"boolean inRange(long v, long min, long max) { return v >= min && v <= max; }","tryCatchPattern":"try { stub.call(request); } catch (StatusRuntimeException e) { if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT && e.getMessage().contains(\"range\")) { /* handle out-of-range */ } }","preventionTips":["Encode protocol-spec ranges as constants and validate client-side","Use typed ID classes rather than raw ints","Re-check ranges when upgrading the server proto"],"tags":["grpc","validation","range"],"backgroundTag":"value-out-of-range","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T14:17:13.074Z"}