Extension SessionHooks missing onPostToolUseFailure handler — failed tool results are invisible
@patniko ci sta già lavorando.
Dal 19/5/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
Summary
Extensions registering hook handlers via joinSession({ hooks }) cannot observe non-success tool results. onPostToolUse only fires for resultType === "success" — failure / rejected / denied / timeout outcomes are silently invisible to extensions.
The underlying CLI does emit a postToolUseFailure hook event for failed tool calls, but the SDK never dispatches it: it is missing from both the SessionHooks interface and the runtime _handleHooksInvoke handler map.
Repro
-
In an extension, register a session via
joinSessionwith handlers for bothonPostToolUseand (attempting)onPostToolUseFailure:import { joinSession } from "@github/copilot-sdk/extension"; const session = await joinSession({ hooks: { onPostToolUse: async (input) => { console.log("post:", input.toolName, input.toolResult?.resultType); }, // Not declared on SessionHooks, but try anyway: onPostToolUseFailure: async (input) => { console.log("post-failure:", input.toolName); }, }, }); -
Have Copilot run a tool that fails (e.g. a shell command that exits non-zero, then a tool that times out, then one that triggers a "rejected" / "denied" outcome).
-
Observe:
onPostToolUsefires only for the successful tool calls.onPostToolUseFailurenever fires for any of the failure modes.
Expected
Extensions should be able to observe all terminal tool outcomes — success, failure, rejected, denied, timeout — through the hooks system. Either:
- Add
onPostToolUseFailuretoSessionHooksand dispatch it from_handleHooksInvoke, or - Always fire
onPostToolUseregardless ofresultType(with theresultTypefield on the input distinguishing outcomes).
Evidence in shipped CLI 1.0.43-0
Path: <copilot-pkg>/copilot-sdk/extension.js (the bundled SDK shipped with the Copilot CLI).
The runtime hook dispatcher exposes only 6 hook types:
async _handleHooksInvoke(hookType, input) {
if (!this.hooks) return void 0;
const handlerMap = {
preToolUse: this.hooks.onPreToolUse,
postToolUse: this.hooks.onPostToolUse,
userPromptSubmitted: this.hooks.onUserPromptSubmitted,
sessionStart: this.hooks.onSessionStart,
sessionEnd: this.hooks.onSessionEnd,
errorOccurred: this.hooks.onErrorOccurred,
};
const handler = handlerMap[hookType];
if (!handler) return void 0; // silently dropped
...
}
The matching TypeScript declaration in copilot-sdk/types.d.ts SessionHooks declares the same 6 handlers, so authors using TypeScript can't even discover the missing types.
Meanwhile, the host CLI (app.js in the same package) builds a hook proxy per connected extension that DOES include postToolUseFailure:
// createHooksProxy(sessionId, connection)
return {
preToolUse: [o => n("preToolUse", o)],
postToolUse: [o => n("postToolUse", o)],
postToolUseFailure: [o => n("postToolUseFailure", o)],
userPromptSubmitted: [o => n("userPromptSubmitted", o)],
sessionStart: [o => n("sessionStart", o)],
sessionEnd: [o => n("sessionEnd", o)],
errorOccurred: [o => n("errorOccurred", o)],
};
And the host's processToolExecutionResult explicitly gates postToolUse on success, routing failures elsewhere:
async processToolExecutionResult(toolName, toolArgs, toolResult) {
let s = (toolResult.resultType === "success"
? await h1(this.getEffectiveHooks()?.postToolUse, ...)
: void 0)?.modifiedResult ?? toolResult;
let a = s.resultType === "failure"
? await this.runPostToolUseFailureHooks(toolName, toolArgs, s)
: void 0;
...
}
So the CLI sends hooks.invoke requests with hookType: "postToolUseFailure" to the extension over IPC, but the SDK's _handleHooksInvoke doesn't recognize that hook type and returns undefined without invoking any handler.
The full set of hook types the CLI knows about (app.js):
new Set([
"sessionStart", "sessionEnd", "userPromptSubmitted",
"preToolUse", "postToolUse", "postToolUseFailure",
"errorOccurred",
"agentStop", "subagentStop", "subagentStart",
"preCompact", "permissionRequest", "notification",
])
The SDK's hooks API exposes 6 of these to extensions. Several of the missing ones (like permissionRequest) are intentionally exposed via different APIs (onPermissionRequest in the JoinSessionConfig), so this report intentionally focuses on the most clear-cut gap: postToolUseFailure.
Impact
Extensions that want to observe or react to tool failures — for telemetry, replay buffers, fault-injection tests, or UI that visualizes tool execution — have no reliable way to do so. Since post-only-on-success silently drops the failure cases, extensions can wrongly report "no result" or, if pairing pre with post, leak resources tracking calls whose post will never come.
Suggested fix
Add the missing handler to _handleHooksInvoke's map and to the SessionHooks TypeScript interface:
export interface SessionHooks {
onPreToolUse?: PreToolUseHandler;
onPostToolUse?: PostToolUseHandler;
+ onPostToolUseFailure?: PostToolUseFailureHandler;
onUserPromptSubmitted?: UserPromptSubmittedHandler;
onSessionStart?: SessionStartHandler;
onSessionEnd?: SessionEndHandler;
onErrorOccurred?: ErrorOccurredHandler;
}
const handlerMap = {
preToolUse: this.hooks.onPreToolUse,
postToolUse: this.hooks.onPostToolUse,
+ postToolUseFailure: this.hooks.onPostToolUseFailure,
userPromptSubmitted: this.hooks.onUserPromptSubmitted,
sessionStart: this.hooks.onSessionStart,
sessionEnd: this.hooks.onSessionEnd,
errorOccurred: this.hooks.onErrorOccurred,
};
The input shape can mirror PostToolUseHookInput (toolName + toolArgs + toolResult), since the CLI's emit site already provides the same payload.
Environment
@github/copilot1.0.43-0(Windowswin32-x64package).- Bundled SDK at
<install>/copilot-sdk/extension.js. - Confirmed by inspecting the shipped JS and the TypeScript declarations in the same package.
- Lingua principale
- Java
- Stelle
- 10.5k
- Fork
- 1.5k
- Merge medio
- 1g 9h
- PR unite (30g)
- 130
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di github/copilot-sdk
-
agentic-workflows
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
github/copilot-sdk#2760 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
github/copilot-sdk#2759 ·
-
documentation
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
github/copilot-sdk#2758 ·
-
agentic-workflows
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
github/copilot-sdk#2709 · 1 commento ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 78/100
github/copilot-sdk#2673 ·
Tutte le issue di github/copilot-sdk
Issue simili
-
executions.Query — startDate and timeRange filters are sent with inverted comparison operators Apertaarea/plugin
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
kestra-io/plugin-kestra#190 ·
-
litertlm-android AAR ships no consumer ProGuard rules → "mid == null" SIGABRT in minified apps Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
google-ai-edge/LiteRT-LM#3739 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
integra-team-red/meet-map#249 ·
-
[Studio][Bug] Cancelled create-user dialog keeps the password and admin switch for the next attempt Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
apache/rocketmq-dashboard#5064 ·
-
Consent portal: creating a duplicate Purpose shows a generic error instead of "already exists" Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
wso2/dpdp-accelerator#287 ·