You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A registered custom client tool (a Microsoft.Extensions.AIAIFunction, exposed via SessionConfig.Tools + AvailableTools = new ToolSet().AddCustom("*")) is never invoked. The model emits a valid tool call with valid arguments and the PreToolUse hook fires, but the runtime then reports PostToolUseFailure with the opaque message "Tool execution failed" and a failed ToolExecutionComplete — without ever calling the tool's AIFunction.InvokeAsync / DelegatingAIFunction.InvokeCoreAsync.
The same code/config works in a single‑user local run but fails consistently in a deployed multi‑user Linux server.
Environment
GitHub.Copilot.SDK (.NET):1.0.1
Mode:CopilotClientMode.Empty (in‑process; no CLI subprocess)
ExternalToolRequested — runtime requests the client tool (args still valid).
Hook.PostToolUseFailure — res = tool_invocation_failed, Error = "Tool execution failed" (21 chars; no exception type/message/stack).
ExternalToolCompleted → HookStart/HookEnd.
ToolExecutionComplete — res = tool execution failed.
AssistantTurnEnd.
The registered AIFunction handler is never entered between steps 4 and 5. The model then retries and fails identically (a second PreToolUse → ExternalToolRequested → PostToolUseFailure cycle).
Trace (SDK event names only — no identifiers/payloads)
Both red rows are the failure: Hook.PostToolUseFailure (tool_invocation_failed) and SdkEvent.ToolExecutionComplete (tool execution failed). Note Hook.PreToolUse and PermissionCompleted immediately above succeed, and ExternalToolRequested/ExternalToolCompleted bracket the failure — yet the client handler is never called.
How we know the handler is never invoked
Every custom tool is wrapped in a DelegatingAIFunction that logs on entry (Debug) and on throw (Error) inside InvokeCoreAsync. With the host and SDK at trace/all verbosity, neither the entry nor the throw log is ever emitted for the failing call.
The handler's own first‑line "invoking" log (Information) is also absent.
⇒ The failure happens upstream of the handler, inside the runtime's client‑tool dispatch.
Arguments are valid (host‑side binding ruled out)
The model produced valid args on multiple retries, e.g.:
The single required parameter is always present; optional params carry = null defaults. So this is not a host‑side argument‑binding / schema‑validation failure (those would surface inside InvokeCoreAsync, which is never reached).
Observability gap
The only failure signal the host receives is PostToolUseFailureHookInput.Error.ToString() == "Tool execution failed" — no exception, inner error, stack, or dispatch reason. Even with LogLevel = all and Logger bridged to ILogger, the runtime emits no diagnostic explaining why the client tool was not dispatched. (Related observability gap: #1220.)
Questions for maintainers
Under what conditions does the runtime fail a client tool with "Tool execution failed"without invoking the registered handler? (dispatch/lookup/marshaling failure, callId correlation, multi‑session tool‑registry issues, etc.)
Where does the runtime log the underlying reason, and how can we surface it (config / log category / event) from the .NET SDK in a server deployment? LogLevel="all" + bridged Logger did not surface it.
Is there a known difference between single‑session and concurrent multi‑session (multi‑user server) client‑tool dispatch in 1.0.1?
varclient=factory.Create(newCopilotClientOptions{Mode=CopilotClientMode.Empty,LogLevel=newCopilotLogLevel("all"),Logger=logger,});awaitclient.StartAsync();AIFunctiontool=AIFunctionFactory.Create(([Description("Short required text")]stringrequiredProp,[Description("Optional")]string?optionalProp=null,CancellationTokenct=default)=>{logger.LogInformation("HANDLER ENTERED");// never logged in the failing envreturnValueTask.FromResult<object?>("ok");},name:"my_custom_tool",description:"Example custom client tool");varconfig=newSessionConfig{Model="<azure-openai-deployment>",// Azure OpenAI BYOK, wireApi = responsesTools=newList<AIFunctionDeclaration>{tool},AvailableTools=newToolSet().AddBuiltIn(BuiltInTools.Isolated).AddCustom("*"),Hooks=/* PreToolUse + PostToolUse + PostToolUseFailure handlers */,};// Drive a prompt that makes the model call my_custom_tool.// Observed: PreToolUse fires, then PostToolUseFailure Error="Tool execution failed";// "HANDLER ENTERED" is never logged.
Note: we cannot share a fully self‑contained repro yet — it reproduces only in our concurrent multi‑user Linux server and not in a single‑user local session with identical provider/model/tool config. Happy to provide more detail, traces (privately), or test a patch.
Summary
A registered custom client tool (a
Microsoft.Extensions.AIAIFunction, exposed viaSessionConfig.Tools+AvailableTools = new ToolSet().AddCustom("*")) is never invoked. The model emits a valid tool call with valid arguments and thePreToolUsehook fires, but the runtime then reportsPostToolUseFailurewith the opaque message"Tool execution failed"and a failedToolExecutionComplete— without ever calling the tool'sAIFunction.InvokeAsync/DelegatingAIFunction.InvokeCoreAsync.The same code/config works in a single‑user local run but fails consistently in a deployed multi‑user Linux server.
Environment
1.0.1CopilotClientMode.Empty(in‑process; no CLI subprocess)provider.type = "azure"), wire APIresponses, api‑version2025-04-01-previewCopilotClientOptions.LogLevel = "all",Loggerbridged to hostILoggerObserved event sequence
For a single failing tool call the SDK emits (in order):
AssistantMessage/AssistantReasoning— model decides to call the custom tool.ToolExecutionStart→HookStart/HookEnd→Hook.PreToolUse— pre-tool hook runs and succeeds (tool name + input args present and correct).PermissionRequested→PermissionCompleted— permission check succeeds.ExternalToolRequested— runtime requests the client tool (args still valid).Hook.PostToolUseFailure—res = tool_invocation_failed,Error = "Tool execution failed"(21 chars; no exception type/message/stack).ExternalToolCompleted→HookStart/HookEnd.ToolExecutionComplete—res = tool execution failed.AssistantTurnEnd.The registered
AIFunctionhandler is never entered between steps 4 and 5. The model then retries and fails identically (a secondPreToolUse → ExternalToolRequested → PostToolUseFailurecycle).Trace (SDK event names only — no identifiers/payloads)
Both red rows are the failure:
Hook.PostToolUseFailure(tool_invocation_failed) andSdkEvent.ToolExecutionComplete(tool execution failed). NoteHook.PreToolUseandPermissionCompletedimmediately above succeed, andExternalToolRequested/ExternalToolCompletedbracket the failure — yet the client handler is never called.How we know the handler is never invoked
DelegatingAIFunctionthat logs on entry (Debug) and on throw (Error) insideInvokeCoreAsync. With the host and SDK at trace/allverbosity, neither the entry nor the throw log is ever emitted for the failing call.Arguments are valid (host‑side binding ruled out)
The model produced valid args on multiple retries, e.g.:
{"RequiredStringProp":"<short text>"} {"RequiredStringProp":"<short text>","OptionalProp":null}The single required parameter is always present; optional params carry
= nulldefaults. So this is not a host‑side argument‑binding / schema‑validation failure (those would surface insideInvokeCoreAsync, which is never reached).Observability gap
The only failure signal the host receives is
PostToolUseFailureHookInput.Error.ToString()=="Tool execution failed"— no exception, inner error, stack, or dispatch reason. Even withLogLevel = allandLoggerbridged toILogger, the runtime emits no diagnostic explaining why the client tool was not dispatched. (Related observability gap: #1220.)Questions for maintainers
"Tool execution failed"without invoking the registered handler? (dispatch/lookup/marshaling failure,callIdcorrelation, multi‑session tool‑registry issues, etc.)LogLevel="all"+ bridgedLoggerdid not surface it.1.0.1?tool.callto the SDK — reported for Anthropic/Python, where it was noted OpenAI BYOK worked). Is there an analogous path for Azure OpenAI BYOK with theresponseswire API on the .NET runtime?Minimal repro sketch (generic)