{"record":{"id":"636068bc4e1f8992","repo":"signalapp/Signal-Server","slug":"call-end-reason-not-specified","errorCode":null,"errorMessage":"Call end reason not specified","messagePattern":"Call end reason not specified","errorType":"validation","errorClass":"CallQualityInvalidArgumentsException","httpStatus":null,"severity":"warning","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManager.java","lineNumber":173,"sourceCode":"        });\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":155,"sourceCodeEnd":177,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/metrics/CallQualitySurveyManager.java#L155-L177","documentation":"validateRequest in CallQualitySurveyManager rejects survey submissions whose callEndReason is null or blank. The end reason (e.g. hangup, timeout, declined) is required to interpret the survey response. The exception names the callEndReason field so the client knows what to supply.","triggerScenarios":"submitCallQualitySurvey invoked with a request whose getCallEndReason() returns null, empty, or whitespace.","commonSituations":"Clients that terminate calls through paths that never set an end reason; survey submitted after abnormal termination (network drop, app kill); API consumers hand-building request bodies.","solutions":["Populate callEndReason with the actual termination reason before submitting","Ensure all call-teardown code paths record a default end reason (e.g. local hangup)","Validate the survey object on the client before the HTTP call"],"exampleFix":"// before\nrequest.setCallEndReason(null);\n// after\nrequest.setCallEndReason(\"LOCAL_HANGUP\");","handlingStrategy":"validation","validationCode":"if (!request.callEndReason || request.callEndReason.trim() === '') { throw new Error('callEndReason must be a non-empty value'); }","typeGuard":"const hasCallEndReason = (r) => typeof r.callEndReason === 'string' && r.callEndReason.trim().length > 0;","tryCatchPattern":"try { await submitCallQualitySurvey(request); } catch (CallQualityInvalidArgumentsException e) { if ('callEndReason'.equals(e.getField())) { request.setCallEndReason('UNKNOWN'); resubmit(); } }","preventionTips":["Guarantee every call teardown path records an end reason, defaulting to UNKNOWN","Centralize end-reason assignment in the call manager","Validate the survey object before submission"],"tags":["api","validation","missing-field"],"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"}