Summary
The egress grant sealed in service/src/egress-grant.ts lists every input file (id, storage_session_id, name) plus read_sessions, and is sent to the egress gateway in the X-CodeAPI-Egress-Grant header. Its size grows with the number of input files and the length of their names. The gateway is started with bun run src/egress-gateway.ts (service/Dockerfile.egress-gateway), so Bun's default 16 KiB header limit applies.
When the sealed grant exceeds that limit, the gateway answers 431 Request Header Fields Too Large to every egress call from the sandbox runner. The first of these is the .dirkeep marker fetch in Job.prime(), so the execution fails with a generic sandbox_execution_failed 500.
The default SANDBOX_MAX_INPUT_FILES is 256 (api/src/config.ts), and a grant listing 256 files is well over 16 KiB. We saw failures start somewhere between 70 and 120 input files, depending on file name length.
Environment
- code-interpreter built from
737f498 plus an unrelated Redis retry patch. main (cf0e668) has the same code.
- Kubernetes,
hardenedSandboxMode: true, egressGrant.ledgerRequired: true, libkrun microVM (executionProfile: default)
- Client: LibreChat with
@librechat/agents 3.7.17, which sends every file in the conversation as an input file on each agent turn
Steps to reproduce
- Put about 120 files in one session, or send an execution request with more than about 100 entries in
files with typical file names.
- Run any bash or python execution that uses them.
Expected
The execution runs. If there is a real limit, the request fails with a 4xx that names it, for example "too many input files".
Actual
sandbox-runner:
{"level":40,"msg":"Failed to auto-load .dirkeep markers","err":{"message":"HTTP error loading .dirkeep markers: 431","stack":"Error: HTTP error loading .dirkeep markers: 431\n at fetchSessionMarkers (/sandbox_api/.build/index.js:319:6135)"}}
{"level":50,"msg":"Error executing job","err":{"message":"HTTP error loading .dirkeep markers: 431"}}
service-worker / api:
{"level":"error","message":"Error processing job Request failed with status code 500","responseError":"sandbox_execution_failed","status":500,"url":"http://codeapi-sandbox-runner:2000/api/v2/execute"}
{"level":"error","message":"... Error during execution: Error from sandbox: [sandbox_execution_failed] Sandbox execution failed"}
The client only gets a 500, which @librechat/agents shows as "Code execution is temporarily unavailable. Please retry."
Workaround
Start the gateway with a larger limit:
command: ["bun", "--max-http-header-size=131072", "run", "src/egress-gateway.ts"]
Checked in the pod: a 40 KB header to /live returns 200 with the flag and 431 without it.
Possible fixes
Any one of these would solve it:
- Make the header limit configurable in the chart and Dockerfile (for example
EGRESS_GATEWAY_MAX_HEADER_BYTES), with a default that fits SANDBOX_MAX_INPUT_FILES.
- Keep the file list out of the header: send the grant in the request body, or store the full claims server-side (the egress ledger already exists) and seal only a reference.
- At least, map a 431 from the gateway to an error that says what went wrong, not a generic
sandbox_execution_failed.
Summary
The egress grant sealed in
service/src/egress-grant.tslists every input file (id, storage_session_id, name) plusread_sessions, and is sent to the egress gateway in theX-CodeAPI-Egress-Grantheader. Its size grows with the number of input files and the length of their names. The gateway is started withbun run src/egress-gateway.ts(service/Dockerfile.egress-gateway), so Bun's default 16 KiB header limit applies.When the sealed grant exceeds that limit, the gateway answers 431 Request Header Fields Too Large to every egress call from the sandbox runner. The first of these is the
.dirkeepmarker fetch inJob.prime(), so the execution fails with a genericsandbox_execution_failed500.The default
SANDBOX_MAX_INPUT_FILESis 256 (api/src/config.ts), and a grant listing 256 files is well over 16 KiB. We saw failures start somewhere between 70 and 120 input files, depending on file name length.Environment
737f498plus an unrelated Redis retry patch.main(cf0e668) has the same code.hardenedSandboxMode: true,egressGrant.ledgerRequired: true, libkrun microVM (executionProfile: default)@librechat/agents3.7.17, which sends every file in the conversation as an input file on each agent turnSteps to reproduce
fileswith typical file names.Expected
The execution runs. If there is a real limit, the request fails with a 4xx that names it, for example "too many input files".
Actual
sandbox-runner:
{"level":40,"msg":"Failed to auto-load .dirkeep markers","err":{"message":"HTTP error loading .dirkeep markers: 431","stack":"Error: HTTP error loading .dirkeep markers: 431\n at fetchSessionMarkers (/sandbox_api/.build/index.js:319:6135)"}} {"level":50,"msg":"Error executing job","err":{"message":"HTTP error loading .dirkeep markers: 431"}}service-worker / api:
{"level":"error","message":"Error processing job Request failed with status code 500","responseError":"sandbox_execution_failed","status":500,"url":"http://codeapi-sandbox-runner:2000/api/v2/execute"} {"level":"error","message":"... Error during execution: Error from sandbox: [sandbox_execution_failed] Sandbox execution failed"}The client only gets a 500, which
@librechat/agentsshows as "Code execution is temporarily unavailable. Please retry."Workaround
Start the gateway with a larger limit:
Checked in the pod: a 40 KB header to
/livereturns 200 with the flag and 431 without it.Possible fixes
Any one of these would solve it:
EGRESS_GATEWAY_MAX_HEADER_BYTES), with a default that fitsSANDBOX_MAX_INPUT_FILES.sandbox_execution_failed.