{"record":{"id":"ee517244ed38acc6","repo":"signalapp/Signal-Server","slug":"end-timestamp-not-specified","errorCode":null,"errorMessage":"End timestamp not specified","messagePattern":"End timestamp not specified","errorType":"validation","errorClass":"CallQualityInvalidArgumentsException","httpStatus":null,"severity":"warning","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManager.java","lineNumber":165,"sourceCode":"            .build()), pubSubCallbackExecutor)\n        .whenComplete((_, throwable) -> {\n          if (throwable != null) {\n            logger.warn(\"Failed to publish call quality survey pub/sub message\", throwable);\n          }\n\n          Metrics.counter(PUB_SUB_MESSAGE_COUNTER_NAME, \"success\", String.valueOf(throwable == null))\n              .increment();\n        });\n  }\n\n  @VisibleForTesting\n  static void validateRequest(final SubmitCallQualitySurveyRequest request) throws CallQualityInvalidArgumentsException {\n    if (request.getStartTimestamp() == 0) {\n      throw new CallQualityInvalidArgumentsException(\"Start timestamp not specified\", \"startTimestamp\");\n    }\n\n    if (request.getEndTimestamp() == 0) {\n      throw new CallQualityInvalidArgumentsException(\"End timestamp not specified\", \"endTimestamp\");\n    }\n\n    if (StringUtils.isBlank(request.getCallType())) {\n      throw new CallQualityInvalidArgumentsException(\"Call type not specified\", \"callType\");\n    }\n\n    if (StringUtils.isBlank(request.getCallEndReason())) {\n      throw new CallQualityInvalidArgumentsException(\"Call end reason not specified\", \"callEndReason\");\n    }\n  }\n}\n","sourceCodeStart":147,"sourceCodeEnd":177,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManager.java#L147-L177","documentation":"validateRequest in CallQualitySurveyManager rejects survey submissions whose endTimestamp is 0. A survey without a call end time is considered incomplete, so the manager throws CallQualityInvalidArgumentsException naming the endTimestamp field. This is a client-input validation failure, not a server error.","triggerScenarios":"submitCallQualitySurvey invoked with a request whose getEndTimestamp() returns 0, i.e. the JSON body omitted endTimestamp or set it to 0.","commonSituations":"Clients that crash or are killed before the call ends and submit a partial survey; test harnesses constructing minimal request objects; field-name mismatches leaving the long defaulted to 0.","solutions":["Include a valid endTimestamp in the survey request body","Only submit the survey after the call has actually terminated and the end time is known","Check that the client serializes the field as endTimestamp so it deserializes correctly server-side"],"exampleFix":"// before\n{\"startTimestamp\": 1699999400, \"callType\": \"audio\"}\n// after\n{\"startTimestamp\": 1699999400, \"callType\": \"audio\", \"endTimestamp\": 1700000000}","handlingStrategy":"validation","validationCode":"if (!request.endTimestamp || request.endTimestamp === 0) { throw new Error('endTimestamp is required before submitting a call quality survey'); }","typeGuard":"const hasEndTimestamp = (r) => typeof r.endTimestamp === 'number' && r.endTimestamp > 0;","tryCatchPattern":"try { await submitCallQualitySurvey(request); } catch (CallQualityInvalidArgumentsException e) { if ('endTimestamp'.equals(e.getField())) { setEndTimestamp(Date.now()) ; resubmit(); } }","preventionTips":["Only submit the survey after the call teardown completes and end time is known","Persist call end time in all teardown paths, including error/crash recovery","Add a client-side pre-submit check for all required fields"],"tags":["api","validation","missing-field"],"backgroundTag":"missing-required-argument","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"}