@@ -150,6 +150,36 @@ func TestPostAgent_ReturnsOKOnContextCanceled(t *testing.T) {
150 t.Fatal("dispatched run was never entered")
150 t.Fatal("dispatched run was never entered")
152 close(coord.release)
152 close(coord.release)
154+ // Wait for the dispatched run to fully return. Backend.runAgent
155+ // swallows context.Canceled, so it must not publish a
156+ // notify.TypeAgentError. Publishing would dereference the synthetic
157+ // workspace's nil notification broker and crash this goroutine,
158+ // which is the explicit guard that a cancel produces no top-level
160+ require.Eventually(t, func() bool {
161+ return coord.ranCount.Load() == 1
162+ }, 2*time.Second, 10*time.Millisecond)
165+// TestHandleError_ContextCanceledFallsThroughTo500 documents the step 8
166+// cleanup: the old context.Canceled special case in handleError was
167+// removed because runtime cancellation of an agent run can no longer
168+// reach handleError. The agent-prompt handler returns 202 before the run
169+// starts (fire-and-forget SendMessage) and Backend.runAgent swallows
170+// context.Canceled. Any context.Canceled that still reaches handleError
171+// is therefore an unexpected synchronous error and falls through to the
172+// default 500 like any other.
173+func TestHandleError_ContextCanceledFallsThroughTo500(t *testing.T) {
176+ c := &controllerV1{server: &Server{}}
177+ rec := httptest.NewRecorder()
178+ req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
180+ c.handleError(rec, req, context.Canceled)
182+ require.Equal(t, http.StatusInternalServerError, rec.Code)
155// TestPostAgent_DetachesRequestContext verifies that the dispatched run
185// TestPostAgent_DetachesRequestContext verifies that the dispatched run