Skip to content

Reject malformed timeout headers with invalid_argument - #351

Merged
stefanvanburen merged 1 commit into
mainfrom
svanburen/reject-invalid-timeouts
Sep 25, 2026
Merged

stefanvanburen merged 1 commit into
mainfrom
svanburen/reject-invalid-timeouts

Conversation

@stefanvanburen

Copy link
Copy Markdown
Member

Both timeout parsers passed the header to int(), so connect-timeout-ms: -1 and grpc-timeout: -5m were accepted and produced a negative ctx.timeout_ms. connectrpc/connect-go#982 closed the same gap for connect-timeout-ms last week. This goes a step further and requires ASCII digits, matching the spec and connect-es's /^\d{1,10}$/, so +5 and -0 are rejected here where connect-go still accepts them.

A malformed grpc-timeout previously raised ValueError, which surfaced as a 500 unknown and an unhandled exception in the ASGI server's logs. It is now invalid_argument, as in connect-go.

The error is still written as a Connect JSON response, so a gRPC client sees HTTP 400 and maps it to internal, which is also what grpc-go returns for a malformed grpc-timeout. Sending it as a trailers-only gRPC response instead would need the pre-stream error path in both servers to know the protocol; I left that out to keep this small.

Both timeout parsers passed the header to `int()`, which also accepts a
sign, surrounding whitespace, and underscores. A negative
`connect-timeout-ms` or `grpc-timeout` was therefore accepted, where
connectrpc/connect-go#982 now rejects it. Both parsers now require ASCII
digits.

`_parse_timeout` raised `ValueError`, so a malformed `grpc-timeout`
became a 500 `unknown` response and propagated to the ASGI server as an
unhandled exception. It now raises `ConnectError(INVALID_ARGUMENT)`, as
connect-go does. The response is still a Connect error (HTTP 400 with a
JSON body), which a gRPC client reads as `internal`; grpc-go answers a
malformed `grpc-timeout` with `internal` too.

Signed-off-by: Stefan VanBuren <stefan@vanburen.xyz>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

going to fix this being marked as "generated" separately...

digits = timeout[:-1]
# int() also accepts a sign, whitespace, and underscores.
if not (digits.isascii() and digits.isdigit()):
raise ConnectError(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a tad funky to do this as connect errors in gRPC-land, but seems reasonable to match connect-go.

@stefanvanburen
stefanvanburen marked this pull request as ready for review September 25, 2026 01:29

@anuraaga anuraaga left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@stefanvanburen
stefanvanburen enabled auto-merge (squash) September 25, 2026 12:28
@stefanvanburen
stefanvanburen merged commit bd3d708 into main Sep 25, 2026
24 checks passed
@stefanvanburen
stefanvanburen deleted the svanburen/reject-invalid-timeouts branch September 25, 2026 12:28
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.

2 participants