Skip to content

fix(functions): raise FunctionsRelayError for x-relay-error responses - #1638

Open
hsusul wants to merge 1 commit into
supabase:mainfrom
hsusul:fix/functions-relay-error-header
Open

hsusul wants to merge 1 commit into
supabase:mainfrom
hsusul:fix/functions-relay-error-header

Conversation

@hsusul

@hsusul hsusul commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Bug fix.

What is the current behavior?

Fixes #1637

invoke() is supposed to raise FunctionsRelayError when the Supabase relay cannot reach the Edge Function. Two things prevent that:

  1. The client reads x-relay-header. The relay, supabase-js, supabase-swift, and supabase_flutter all use x-relay-error.
  2. _request calls raise_for_status() before any relay-header check. Relay failures are non-2xx (supabase-swift#1112), so they become FunctionsHttpError even if the header name were corrected.

The existing tests mock x-relay-header on a 200, so they pass while except FunctionsRelayError is dead in production. The generated sync client has the same code.

What is the new behavior?

_request checks x-relay-error == "true" before raise_for_status() and raises FunctionsRelayError with the response status, matching supabase-js:

const isRelayError = response.headers.get('x-relay-error')
if (isRelayError && isRelayError === 'true') {
  throw new FunctionsRelayError(response)
}

A function-side 4xx/5xx without that header is still FunctionsHttpError. Public invoke() arguments are unchanged.

Open PRs #1576 and #1600 touch nearby error-body parsing in the same files but still read x-relay-header; this change is the wire-format/order fix.

Tests

test_invoke_with_relay_error now sends the real x-relay-error header. New test_invoke_relay_error_on_non_2xx_status returns 546 plus x-relay-error: true and asserts FunctionsRelayError (not FunctionsHttpError). Both fail on main @ bb7ecc5 and pass here, async and sync.

Validation

  • Focused tests, red on main (DID NOT RAISE / FunctionsHttpError), green after: 4 passed.
  • uv run --package supabase_functions pytest: 67 passed, 1 skipped (test_strenum_import_python_310_and_below on 3.13).
  • uv run --package supabase_functions mypy src/supabase_functions tests: no issues in 16 source files.
  • uv run ruff check / uv run ruff format --check on src/functions: clean.
  • git diff --check: clean.

Functions tests are unit tests with httpx mocks and do not need Docker or the Supabase CLI.

The relay signals infrastructure failures with x-relay-error, including
on non-2xx statuses. invoke() looked at x-relay-header after raise_for_status,
so FunctionsRelayError never fired for real relay failures.
@hsusul
hsusul requested review from a team and o-santi as code owners September 14, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

invoke() never raises FunctionsRelayError for real relay failures

1 participant