fix(gateway): fail fast on undeliverable approval notifications - #19803
fix(gateway): fail fast on undeliverable approval notifications#19803konsisumer wants to merge 1 commit into
Conversation
|
Rebased onto Addressing maintainer feedback
|
90a84a9 to
08fa2ad
Compare
|
Rebased onto origin/main and reconciled the gateway/run.py conflict: upstream switched the approval text-send to |
08fa2ad to
1ebbda6
Compare
austinpickett
left a comment
There was a problem hiding this comment.
Please use PULL_REQUEST_TEMPLATE.md
There was a problem hiding this comment.
Pull request overview
Fixes a 5-minute hang in the gateway when a dangerous command requires approval but the platform adapter (e.g., APIServerAdapter) cannot push the prompt. Previously _approval_notify_sync silently discarded SendResult(success=False), leaving the agent thread blocked on event.wait() with no way to resolve. The change makes the notify callback raise when delivery fails, so the existing handler in tools/approval.py drains the queue and returns BLOCKED immediately.
Changes:
- In
gateway/run.py,_approval_notify_syncnow raises whensafe_schedule_threadsafeyields no future, whensend()raises, or when the returnedSendResult.successis false. - Adds regression test
test_notify_cb_failure_blocks_fast_without_waiting_for_timeoutthat verifies the guard returnsBLOCKEDsynchronously and drains_gateway_queues.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| gateway/run.py | Propagate adapter send failures from _approval_notify_sync so the approval path fails fast instead of hanging. |
| tests/tools/test_approval_plugin_hooks.py | New regression test asserting BLOCKED result and queue drain when notify_cb raises. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Closing — this PR removes the silent-swallow path in |
What does this PR do?
Stops the 5-minute hang when a dangerous command needs approval but the platform adapter can't deliver the prompt to the user.
_approval_notify_sync(ingateway/run.py) used to swallowSendResult(success=False)and exceptions raised byadapter.send(). The notify_cb returned normally, the approval entry stayed queued, and the agent thread blocked onentry.event.wait(timeout=gateway_timeout)for the full 5 minutes — the user never saw the prompt, and there was no path to resolve it via/approveor/deny.The fallback
send()result is now checked: if scheduling yields no future,send()raises, orSendResult.successis False, we log and re-raise. The existing safety net intools.approval.check_all_command_guardsalready catches exceptions fromnotify_cb, drains the entry from_gateway_queues, and returns aBLOCKED: Failed to send approval request to userresult — we just have to reach it. Concretely fixes the/backgroundflow on the API Server adapter (APIServerAdapter.sendalways returnsSendResult(success=False, ...)), but applies to any adapter where push delivery is unavailable.Related Issue
Fixes #19731
Related: #6059 — implements the dashboard SSE approval stream for the API Server (the long-term push path). This PR is complementary: even once #6059 lands,
_approval_notify_syncshould fail fast onSendResult(success=False)rather than block onentry.event.wait(), so any future adapter without a push channel surfaces a clearBLOCKEDinstead of a hang.Type of Change
Changes Made
gateway/run.py: in the approval text-send fallback, raise whensafe_schedule_threadsafereturns no future, whensend()raises, or when the returnedSendResult.successis false — so the approval guard fails fast withBLOCKEDinstead of waiting outgateway_timeout.tests/tools/test_approval_plugin_hooks.py: addstest_notify_cb_failure_blocks_fast_without_waiting_for_timeout, which registers a notify_cb mimicking the API Server failure and asserts the guard returnsBLOCKEDwell under the configuredgateway_timeout.How to Test
pytest tests/tools/test_approval_plugin_hooks.py -q(the new regression test plus the existing suite).pytest tests/tools/test_approval_plugin_hooks.py tests/gateway/test_approve_deny_commands.py tests/gateway/test_background_command.py tests/gateway/test_session_boundary_security_state.py -q(related approval and background-command suites).Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AAddressing maintainer feedback
PULL_REQUEST_TEMPLATE.mdas requested.pytest tests/ -qcheckbox: the PR's own suite (tests/tools/test_approval_plugin_hooks.py) passes locally (4 passed). The branch was rebased onto currentmain; the earlier CItestfailures were on files this PR does not touch (e.g.test_registry_manifest,test_google_chat,test_teams, model-list tests) caused by a stale base, which the rebase resolves.