Skip to content

fix(api_server): add CORS headers to /v1/runs/{run_id}/events SSE endpoint - #6367

Open
kawanoii wants to merge 3 commits into
NousResearch:mainfrom
kawanoii:fix/run-events-cors
Open

fix(api_server): add CORS headers to /v1/runs/{run_id}/events SSE endpoint#6367
kawanoii wants to merge 3 commits into
NousResearch:mainfrom
kawanoii:fix/run-events-cors

Conversation

@kawanoii

@kawanoii kawanoii commented Apr 9, 2026

Copy link
Copy Markdown

Summary

Fixes CORS headers on the GET /v1/runs/{run_id}/events SSE endpoint.

Root cause: StreamResponse flushes headers immediately on await response.prepare(request), so the CORS middleware cannot inject Access-Control-Allow-Origin headers afterward.

Fix: Pre-resolve the Origin header and compute CORS headers before constructing the StreamResponse, matching the pattern already used in _write_sse_chat_completion. Fixes issue #6358.

Changes

  • gateway/platforms/api_server.py: In _handle_run_events, extract Origin header, call _cors_headers_for_origin() upfront, merge CORS headers into SSE response headers before prepare().
  • tests/gateway/test_api_server.py: 4 new tests in TestRunEventsCORS.

Tests

Unit tests: tests/gateway/test_api_server.py::TestRunEventsCORS
test_run_events_cors_headers_present_for_allowed_origin PASSED
test_run_events_cors_headers_absent_without_origin PASSED
test_run_events_returns_404_for_unknown_run PASSED
test_run_events_streams_sse_event PASSED
4 passed ✅

Live server test (API_SERVER_CORS_ORIGINS=):

  • Cross-origin GET → Access-Control-Allow-Origin: <frontend-origin>
  • OPTIONS preflight → 200 with CORS headers ✅
  • No Origin header → no spurious CORS headers ✅
  • POST /v1/runs → no regression ✅

Notes

  • Trailing slash matters: API_SERVER_CORS_ORIGINS must match the browser's Origin header exactly (no trailing slash).
  • The SSE stream correctly delivers agent lifecycle events: message.delta, reasoning.available, run.completed.

Closes #6358

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

…point

The _handle_run_events method was missing CORS headers on its
StreamResponse, causing cross-origin requests to fail with:

  "No 'Access-Control-Allow-Origin' header is present on the requested resource"

Root cause: StreamResponse flushes headers immediately on prepare(),
so the CORS middleware cannot inject headers after that point.
Fix: Pre-resolve the Origin header and compute CORS headers before
constructing the StreamResponse, matching the pattern used in
_write_sse_chat_completion.

Fixes NousResearch#6358

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
kawanoii and others added 2 commits April 11, 2026 20:40
… endpoint

- test_run_events_cors_headers_present_for_allowed_origin
- test_run_events_cors_headers_absent_without_origin
- test_run_events_returns_404_for_unknown_run
- test_run_events_streams_sse_event

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kawanoii
kawanoii force-pushed the fix/run-events-cors branch from 13cbdc3 to 57188ce Compare April 11, 2026 12:40
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery labels Apr 30, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related: fixes #6358. Prior fix #3573 addressed CORS for /v1/chat/completions SSE but missed the /v1/runs/{run_id}/events endpoint.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused CORS regression fix. Current main still exhibits the reported ordering problem: gateway/platforms/api_server.py:4512-4520 prepares the run-events StreamResponse before the middleware's post-handler update at gateway/platforms/api_server.py:569-572. The proposed construction matches the existing pre-prepare implementation in _write_sse_chat_completion at gateway/platforms/api_server.py:2418-2434.

The handler has moved to gateway/platforms/api_server.py:4494 since the PR's base, so this needs a mechanical relocation during salvage rather than a clean cherry-pick.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: /v1/runs/{run_id}/events SSE endpoint missing CORS headers causes cross-origin requests to fail

3 participants