{"record":{"id":"4df74ebe3e74cf12","repo":"microsoft/aspire","slug":"callback-callbackid-timed-out-after-s-callbacktimeout","errorCode":null,"errorMessage":"Callback '{callbackId}' timed out after {s_callbackTimeout.TotalSeconds}s","messagePattern":"Callback '(.+?)' timed out after (.+?)s","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.RemoteHost/JsonRpcCallbackInvoker.cs","lineNumber":50,"sourceCode":"    {\n        if (_clientRpc == null)\n        {\n            throw new InvalidOperationException(\"No client connection available for callback invocation\");\n        }\n\n        using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);\n        cts.CancelAfter(s_callbackTimeout);\n\n        try\n        {\n            return await _clientRpc.InvokeWithCancellationAsync<TResult>(\n                \"invokeCallback\",\n                [callbackId, args],\n                cts.Token).ConfigureAwait(false);\n        }\n        catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)\n        {\n            throw new TimeoutException($\"Callback '{callbackId}' timed out after {s_callbackTimeout.TotalSeconds}s\");\n        }\n    }\n\n    /// <inheritdoc />\n    public async Task InvokeAsync(string callbackId, JsonNode? args, CancellationToken cancellationToken = default)\n    {\n        await InvokeAsync<object?>(callbackId, args, cancellationToken).ConfigureAwait(false);\n    }\n}\n","sourceCodeStart":32,"sourceCodeEnd":60,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.RemoteHost/JsonRpcCallbackInvoker.cs#L32-L60","documentation":"When invoking a client callback, JsonRpcCallbackInvoker wraps the call in a linked CancellationTokenSource with a fixed timeout (s_callbackTimeout). If the client does not respond before that timeout (and the caller's own token was not cancelled), the OperationCanceledException is converted into this TimeoutException naming the callback id and timeout in seconds.","triggerScenarios":"Calling callbackInvoker.InvokeAsync<TResult>(callbackId, args) where the connected client never replies to the 'invokeCallback' request within s_callbackTimeout seconds, or is blocked/slow handling it.","commonSituations":"Client stuck processing a long-running UI operation; client event loop blocked by a synchronous wait; client hung or paused (debugger breakpoint); network/pipe congestion delaying the response; client not handling that callbackId at all.","solutions":["Verify the client actually implements and responds to the given callbackId and return promptly.","Check the client for blocking work (sync-over-async, locks, debugger pauses) on its RPC handler thread.","Increase responsiveness or move long work off the callback path; acknowledge quickly then process async if the protocol allows.","Catch TimeoutException on the caller side and degrade gracefully or retry if the callback is best-effort."],"exampleFix":"// before\nvar result = await callbackInvoker.InvokeAsync<bool>(\"confirmDeploy\", args); // hangs then times out\n\n// after\ntry\n{\n    var result = await callbackInvoker.InvokeAsync<bool>(\"confirmDeploy\", args);\n}\ncatch (TimeoutException)\n{\n    // fall back to default behavior when the client is unresponsive\n    var result = false;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await invoker.InvokeAsync<TResult>(callbackId, args, ct); }\ncatch (TimeoutException) { /* fallback/default behavior or retry */ }","preventionTips":["Keep client callback handlers fast; return quickly and defer heavy work.","Never block the client RPC dispatch thread (no sync-over-async, no locks held across awaits).","Log callback start/finish on the client to spot slow handlers before they time out."],"tags":["json-rpc","callback","timeout"],"backgroundTag":"request-timeout","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}