Summary
On Zero 0.3.4, an HTTP handler begins failing when its response grows to
approximately 6 KiB.
The application constructs the complete JSON response successfully in a
caller-owned buffer. Increasing that application buffer from 14 KiB to 30 KiB
does not move the failure point.
Once the response reaches the apparent runtime boundary, the server reports
handler_failed and the HTTP client receives status 500.
This looks like a separate limit inside std.http.listen or its handler
callback machinery, rather than exhaustion of the buffer used by the
application to construct the JSON.
Environment
- Zero:
0.3.4
- Build:
5b3a90a
- Host:
linux-x64
- Backend reported by
zero --version --json: zero-c
- HTTP API:
std.http.listen
- Handler signature:
fn handle(
request: Span<u8>,
response: MutSpan<u8>,
) -> Maybe<Span<u8>>
Application structure
The application creates a JSON roster containing an increasing number of
small records. It first constructs the body in its own caller-owned byte
buffer, then passes that completed body to the standard HTTP response helper.
The relevant structure is equivalent to:
fn handle(
request: Span<u8>,
response: MutSpan<u8>,
) -> Maybe<Span<u8>> {
if std.http.requestIsGet(request, "/api/foo/bar") {
var body_storage: [14000]u8 = [0_u8; 14000]
// The application successfully serializes the complete JSON into
// body_storage here.
let body: Span<u8> = build_roster_json(body_storage)
return std.http.writeJsonResponse(response, 200_u16, body)
}
return std.http.writeJsonError(response, 404_u16, "NOT_FOUND")
}
pub fn main(world: World) -> Void raises {
check std.http.listen(world, 9657_u16)
}
I also tested changing the application-owned builder from:
var body_storage: [14000]u8 = [0_u8; 14000]
to:
var body_storage: [30000]u8 = [0_u8; 30000]
That did not change the observed HTTP failure boundary.
Steps to reproduce
- Start a Zero HTTP server using
std.http.listen.
- Provide an endpoint that returns a JSON array.
- Add small objects to the array over successive requests.
- Request the complete array after each addition.
- Continue until the serialized response approaches approximately 6 KiB.
In our application, a small roster succeeds. After accumulating roughly 36
small crew summaries, the roster request fails:
curl -i http://127.0.0.1:9657/api/foo/bar
The server reports:
handler_failed
The client receives HTTP 500 instead of the valid JSON body.
The records themselves remain persisted and readable individually. Smaller
roster responses continue to work. The failure is specifically associated
with returning the larger aggregate response.
Observed result
-
The application-level JSON builder has sufficient capacity.
-
JSON construction succeeds.
-
Persisted records are intact.
-
Small responses succeed.
-
At approximately 6 KiB, the native HTTP handler fails.
-
Increasing the application builder to 30 KiB does not increase the maximum
HTTP response size.
-
The server surfaces the failure as handler_failed / HTTP 500.
Expected result
One of the following would be expected:
- The handler can return any response that fits in the `response:
by the runtime; or
- The size limit is documented and exposed to the handler; or
- The response helper returns a specific capacity/overflow failure that lets
the application handle the condition.
A hidden lower limit that produces only handler_failed makes it difficult to
determine the supported response size or recover gracefully.
Questions
- What is the actual capacity of the response:
MutSpan<u8> passed to an
std.http.listen handler?
- Is the capacity intended to be approximately 6 KiB?
- Can the handler inspect that capacity using
std.mem.len(response)?
- Is there a supported way to configure a larger HTTP response buffer?
- If the response is too large, could the runtime produce a more specific
diagnostic than handler_failed?
- Is streaming or chunked response output planned?
Additional context
This was discovered while running a conformance suite repeatedly against the
same persisted state. A fresh run remained green because its roster was still
small. Repeated runs accumulated enough valid records to cross the response
boundary.
That initially resembled persisted-state contamination, but the data and
serialization remained valid. The failure persisted at the same approximate
response size even after substantially increasing the application's local JSON
buffer.
Summary
On Zero 0.3.4, an HTTP handler begins failing when its response grows to
approximately 6 KiB.
The application constructs the complete JSON response successfully in a
caller-owned buffer. Increasing that application buffer from 14 KiB to 30 KiB
does not move the failure point.
Once the response reaches the apparent runtime boundary, the server reports
handler_failedand the HTTP client receives status 500.This looks like a separate limit inside
std.http.listenor its handlercallback machinery, rather than exhaustion of the buffer used by the
application to construct the JSON.
Environment
0.3.45b3a90alinux-x64zero --version --json:zero-cstd.http.listenApplication structure
The application creates a JSON roster containing an increasing number of
small records. It first constructs the body in its own caller-owned byte
buffer, then passes that completed body to the standard HTTP response helper.
The relevant structure is equivalent to:
I also tested changing the application-owned builder from:
var body_storage: [14000]u8 = [0_u8; 14000]to:
var body_storage: [30000]u8 = [0_u8; 30000]That did not change the observed HTTP failure boundary.
Steps to reproduce
std.http.listen.In our application, a small roster succeeds. After accumulating roughly 36
small crew summaries, the roster request fails:
curl -i http://127.0.0.1:9657/api/foo/barThe server reports:
handler_failedThe client receives HTTP 500 instead of the valid JSON body.
The records themselves remain persisted and readable individually. Smaller
roster responses continue to work. The failure is specifically associated
with returning the larger aggregate response.
Observed result
The application-level JSON builder has sufficient capacity.
JSON construction succeeds.
Persisted records are intact.
Small responses succeed.
At approximately 6 KiB, the native HTTP handler fails.
Increasing the application builder to 30 KiB does not increase the maximum
HTTP response size.
The server surfaces the failure as handler_failed / HTTP 500.
Expected result
One of the following would be expected:
by the runtime; or
the application handle the condition.
A hidden lower limit that produces only
handler_failedmakes it difficult todetermine the supported response size or recover gracefully.
Questions
MutSpan<u8>passed to anstd.http.listenhandler?std.mem.len(response)?diagnostic than
handler_failed?Additional context
This was discovered while running a conformance suite repeatedly against the
same persisted state. A fresh run remained green because its roster was still
small. Repeated runs accumulated enough valid records to cross the response
boundary.
That initially resembled persisted-state contamination, but the data and
serialization remained valid. The failure persisted at the same approximate
response size even after substantially increasing the application's local JSON
buffer.