Skip to content

Surface Docker error details in sandbox failures - #459

Closed
SSK015 wants to merge 1 commit into
Ephemeral-AI-Lab:mainfrom
SSK015:codex/docker-api-diagnostics
Closed

SSK015 wants to merge 1 commit into
Ephemeral-AI-Lab:mainfrom
SSK015:codex/docker-api-diagnostics

Conversation

@SSK015

@SSK015 SSK015 commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Docker HTTP failures currently omit the retained response body from SDK error
messages. For example, Docker 29.1.3 rejects API v1.54 requests, but callers
cannot see the API-version mismatch.

Include the existing bounded error prefix in Debug output, with control
characters escaped and invalid UTF-8 replaced. Preserve the 1024-byte capture
limit and truncation/read metadata. Add regressions for volume and
container-create refusals, truncation and encoding, and document the Engine
API requirement.

Related to #457.

Validation

  • 32 layerfs-sandbox tests passed, including four new regression tests.
  • 57 Python tooling tests passed in the configured toolchain container.
  • All-target Clippy for sandbox, API and Node packages passed with warnings denied.
  • Workspace formatting, whitespace and the added documentation link checks passed.
  • Real SDK before/after tests on Docker 29.1.3 expose the version-mismatch reason.
  • Sandbox creation and closing on Docker 29.9.2 pass before and after the change.

The real SDK cases each ran once on an isolated Ubuntu 24.04 VM, using the Debian
image digest from #457. Build parallelism was 2 under enforced CPU and memory
limits; each functional case had a 60-second timeout.

Limitations

Docker 29.1.3 still rejects the fixed API v1.54 requests. Request paths and
single-attempt behavior are unchanged.

The full product-boundary check reports four pre-existing violations at
api/binding/src/discovery.rs:607,635, pool.rs:248 and sandbox.rs:504.
Those files and lines match the baseline. The core scope passes (817 files).

Production LOC

102158 -> 102160 (delta +2), using the same pinned tools/production_loc.py
counter on the exact parent and committed trees. Core: 96788 -> 96790;
API: 5370 -> 5370. Reference absent (retired R9c; last counted 65,417 at
tree 498dd1917812); dsh absent (moved out in #428). The commit message
records the counter version, tree identities, method and exclusions.

Render the retained HTTP error prefix in ErrorDiagnostic Debug output so
SDK failures include the daemon's reason. Keep the capture bound and
metadata, escape controls and replace invalid UTF-8. Cover version
refusals, truncation and encoding, and document the v1.54 requirement.

Related to Ephemeral-AI-Lab#457.

Validation: 32 sandbox tests; 57 Python tooling tests; all-target Clippy
for sandbox/API/Node with -D warnings; workspace formatting; real SDK
before/after checks on Docker 29.1.3 and 29.9.2 passed. The full boundary
guard retains four unchanged baseline SDK violations; core scope passes.

Production LOC: 102158 -> 102160 (delta +2)
Scope: first-party core Rust/runtime SQL and API Rust/JavaScript.
Core: 96788 -> 96790; API: 5370 -> 5370; dsh absent (moved out, Ephemeral-AI-Lab#428).
Reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Method: tools/production_loc.py --root <snapshot> --json, pinned at
ad78914 (blob
43a566e). Identical counter/exclusions
for git archive snapshots of the exact first-parent and staged trees:
93e157d ->
a21b767.
Tests, docs, tools, third-party code and test-only source are excluded.
yifanxuaaa added a commit that referenced this pull request Oct 26, 2026
SDK failures for Docker HTTP errors now show the retained bounded error
prefix (at most 1024 bytes, lossy UTF-8, controls escaped), so callers see
the Engine's reason, such as an API v1.54 version refusal (#457). The
owner accepted dropping the old redacted Debug for these diagnostics; docs
71 and 72 are updated. Request paths and single-attempt behaviour are
unchanged; Docker servers that reject v1.54 still fail.

Rebased by the maintainer onto main after #465 moved the sandbox crate to
engine/ and api/how-to-use/ to api/docs/, with one fix commit for the
README link. layerfs-sandbox: 32 tests pass on macOS (cargo 1.85.1), fmt
and Clippy -D warnings clean.

Production LOC: 102162 -> 102164 (delta +2)
  First-parent comparison (main a9d5add -> this merge). core 38241,
  engine 61288 -> 61290, api 2633; reference absent (retired R9c; last
  counted 65,417 at tree 498dd19). Counted with tools/production_loc.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yifanxuaaa

Copy link
Copy Markdown
Contributor

Merged into main as a944016, thanks! Showing the Docker error prefix is accepted. Because #465 moved the sandbox crate to engine/ and api/how-to-use/ to api/docs/, I rebased your commit onto main (4b064df, your authorship kept, LOC line recounted: 102162 -> 102164, +2 under engine) and added one fix commit for the README link (b271270). This PR's branch still holds the pre-rebase commit, so GitHub shows it as closed rather than merged.

@yifanxuaaa yifanxuaaa closed this Oct 26, 2026
yifanxuaaa pushed a commit that referenced this pull request Oct 27, 2026
The Engine client wrote /v1.54 into every request path. On Docker 29.1.3
(Engine API 1.52) every Sandbox create failed at its first request, GET
/v1.54/volumes/{name}, with HTTP 400 "client version 1.54 is too new". #459
made that message visible; this makes the client speak the Engine's version.

The first request of a Docker client now reads the Engine's version once,
with an unversioned GET /version, and every request of that client and its
clones uses /v1.<minor>/ at the Engine's ApiVersion, up to 1.54. The call
sites pass unversioned paths; Docker::send adds the prefix and transmit is
the old send. An Engine below 1.48 is refused before any versioned request:
an Unsupported failure with attempted false whose diagnostic names both
versions ("Engine API 1.47 is older than the 1.48 to 1.54 this client
speaks"). If GET /version cannot be read (not 200, not JSON, no ApiVersion)
the client uses 1.54 as before, so nothing that works now regresses and the
Engine's own refusal still reaches the caller. The probe is a separate read,
never a retry: a request that fails is not sent again at another version.

The floor comes from running it. The version history shows nothing the
client uses changing after 1.30 (copyUIDGID) except the multiplexed-stream
Content-Type on streams (1.42), and nothing in 1.53 or 1.54 touches a field
it sends. But the daemon upload fails on Engines 23.0 to 27.5 (API 1.42 to
1.47), in PUT /containers/{id}/archive: "getent unable to find entry "0:0"
in passwd database", the container's User under copyUIDGID. Engine 28.0
(API 1.48) and every later one tested pass.

Tests: engine_protocol.rs gains four cases on a scripted Engine: a 1.52
Engine gets GET /version, then /v1.52 volume and create requests; a 1.56
Engine gets /v1.54; a 1.47 Engine is refused in the Volume phase with
attempted false and both versions in the failure, /version the only request
sent; a 404 on /version gets /v1.54. All four fail on the old client, which
never asks /version. The peer in support/engine.rs and the Engine in
engine_keepalive.rs answer that first request themselves (API 1.54) and
leave it out of what finish() returns, so the existing tests keep their
assertions; a peer with no responses expects no request at all.
sandbox_create_debug_shows_docker_api_version_refusal_without_negotiating
is renamed ..._without_retrying: it still proves the refused request is not
resent. cargo test -p layerfs-sandbox: 36 pass. Rustfmt and Clippy -D
warnings (workspace without layerfs-node) clean. Real SDK walk (LayerFS.init,
open, sandbox.create, sandbox.close; debian:bookworm-slim sha256:7ff6ba31)
in docker dind: Engine 29.1.3 (1.52) fails on main with the 400 above and
passes now; 29.9.2 (1.56) and 28.0.4 (1.48) to 28.3.3 (1.51) pass; 27.5.1
(1.47) is refused with the message above. No tests deleted.

Production LOC: 102200 -> 102288 (delta +88)
Scope: first-party core, engine and API production source. Core 38241 and
api 2646 unchanged; engine: 61313 -> 61401 (layerfs-sandbox 3329 -> 3417).
Method: tools/production_loc.py --root on git archive of the first parent
and the staged tree (5c6fe6f -> 563acd1).
Reference absent (retired R9c; last counted 65,417 at tree 498dd19).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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