{"record":{"id":"48636df05c0d5089","repo":"dotnet/aspnetcore","slug":"expected-either-error-or-result-to-be-provided","errorCode":null,"errorMessage":"Expected either 'error' or 'result' to be provided, but not both.","messagePattern":"Expected either 'error' or 'result' to be provided, but not both\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/CompletionMessage.java","lineNumber":20,"sourceCode":"// The .NET Foundation licenses this file to you under the MIT license.\n\npackage com.microsoft.signalr;\n\nimport java.util.Map;\n\npublic final class CompletionMessage extends HubMessage {\n    private final int type = HubMessageType.COMPLETION.value;\n    private Map<String, String> headers;\n    private final String invocationId;\n    private final Object result;\n    private final String error;\n\n    public CompletionMessage(Map<String, String> headers, String invocationId, Object result, String error) {\n        if (headers != null && !headers.isEmpty()) {\n            this.headers = headers;\n        }\n        if (error != null && result != null) {\n            throw new IllegalArgumentException(\"Expected either 'error' or 'result' to be provided, but not both.\");\n        }\n        this.invocationId = invocationId;\n        this.result = result;\n        this.error = error;\n    }\n\n    public Map<String, String> getHeaders() {\n        return headers;\n    }\n\n    public Object getResult() {\n        return result;\n    }\n\n    public String getError() {\n        return error;\n    }\n","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/CompletionMessage.java#L2-L38","documentation":"Thrown by the CompletionMessage constructor when both error and result are non-null. A CompletionMessage is the wire reply to an invocation; the protocol allows either a successful result OR an error string, never both. This is an internal invariant guard - applications rarely construct CompletionMessage directly unless writing a custom hub protocol or a test server.","triggerScenarios":"Constructing new CompletionMessage(headers, invocationId, resultObj, errorString) with both the result and error arguments non-null. Happens in custom HubProtocol implementations, test fixtures that craft replies, or buggy server-side code that mirrors CompletionMessage.","commonSituations":"Test doubles for the server that fill both fields by mistake; a custom protocol adapter that copies fields without choosing one branch; upgrading the protocol and forgetting the mutual-exclusion rule.","solutions":["When building a CompletionMessage, choose exactly one: pass result with error==null, or error with result==null.","Add a helper that encodes the rule: CompletionMessage.success(invId, result) vs CompletionMessage.error(invId, err).","If the message came from the wire, validate at the parser: route to error vs result branch before constructing."],"exampleFix":"// before\nnew CompletionMessage(headers, invId, result, \"oops\"); // both set -> throws\n\n// after\nCompletionMessage msg = error != null\n  ? new CompletionMessage(headers, invId, null, error)\n  : new CompletionMessage(headers, invId, result, null);","handlingStrategy":"validation","validationCode":"CompletionMessage success(String invId, Object result) {\n  return new CompletionMessage(null, invId, result, null);\n}\nCompletionMessage failure(String invId, String error) {\n  return new CompletionMessage(null, invId, null, error);\n}\n\n// always pick one branch via the helper - never pass both.","typeGuard":"boolean isValidCompletionInputs(Object result, String error) {\n  return result == null || error == null; // at most one non-null\n}","tryCatchPattern":"try {\n  new CompletionMessage(headers, invId, result, error);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"either 'error' or 'result'\")) {\n    // choose success or error branch and rebuild\n  } else throw e;\n}","preventionTips":["Never set both result and error on a CompletionMessage.","Provide factory helpers that encode the mutual-exclusion rule.","When parsing from the wire, branch on which field is present before constructing."],"tags":["signalr","java","protocol","completion-message","invariant"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}