Skip to content

fix(api): expose sandbox placement failure reason in error message - #3554

Open
AdaAibaby wants to merge 4 commits into
e2b-dev:mainfrom
AdaAibaby:fix/placement-error-diagnosis
Open

fix(api): expose sandbox placement failure reason in error message#3554
AdaAibaby wants to merge 4 commits into
e2b-dev:mainfrom
AdaAibaby:fix/placement-error-diagnosis

Conversation

@AdaAibaby

@AdaAibaby AdaAibaby commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #3553

What

When BestOfK.chooseNode finds no eligible node it returns a FailedToPlaceSandboxError, but create_instance.go only uses the internal Err field and returns the opaque "Failed to place sandbox" to the client.

This PR makes the error actionable:

  1. BestOfK.sample() now counts how many nodes were rejected at each filter stage: not-accepting, cpu-incompatible, label-filtered, excluded.
  2. FailedToPlaceSandboxError.Error() emits a structured message with those counts plus the build CPU constraints and required labels.
  3. create_instance.go propagates err.Error() to ClientMsg so the caller receives the full diagnosis.

Before:

500: Failed to place sandbox

After:

500: Failed to place sandbox: no compatible node found (38 nodes checked: 0 not-accepting, 38 cpu-incompatible, 0 label-filtered, 0 excluded); build cpu: arch=x86_64 family=6 model=207

Why this is safe to expose

The message reveals CPU generation and scheduling labels — information already visible through the admin GET /nodes endpoint. No secrets or topology details are disclosed.

Changes

  • packages/api/internal/orchestrator/placement/placement_best_of_K.go — add nodeRejectionCounts, thread through sample(), populate FailedToPlaceSandboxError, update Error()
  • packages/api/internal/orchestrator/create_instance.goClientMsg now includes err.Error()
  • packages/api/internal/orchestrator/placement/placement_best_of_K_test.go — update assertions for new error format

/cc @jakubno @dobrac @ValentaTomas @arkamar @tvi @tomassrnka Looking forward to your code review.

When no node can host a sandbox, the placement algorithm now counts how
many nodes were rejected at each filter stage (not-accepting, CPU
incompatible, label-filtered, excluded) and includes those counts plus
the build CPU constraints in the error returned to the caller.

The ClientMsg in create_instance.go is updated to surface this
diagnostic string, so clients see why placement failed instead of the
opaque "Failed to place sandbox" message.

Example new message:
  Failed to place sandbox: no compatible node found (38 nodes checked:
  0 not-accepting, 38 cpu-incompatible, 0 label-filtered, 0 excluded);
  build cpu: arch=x86_64 family=6 model=207

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ffeca63463

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/api/internal/orchestrator/create_instance.go Outdated
Use errors.As to distinguish a FailedToPlaceSandboxError (which
contains safe, diagnostic node-rejection counts and CPU constraints)
from other placement failures (timeouts, gRPC internals). Only the
former is formatted into the client-visible message; all other errors
keep the generic "Failed to place sandbox" string.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a6ab5b69e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/api/internal/orchestrator/create_instance.go Outdated
… only

Node counts and CPU model details exposed in the placement error could
allow any team API key to probe cluster topology that is otherwise
restricted to the admin-only GET /nodes route. Revert ClientMsg to the
generic "Failed to place sandbox".

The detailed FailedToPlaceSandboxError (node rejection counts, build
CPU constraints) remains in the internal Err field and is already
recorded by telemetry.ReportError in startSandboxInternal, making it
findable in server logs without exposing it to callers.
telemetry.ReportError writes to OTel spans only, so placement failures
were invisible in Loki. Add an explicit logger.L().Error at the
placement failure site with sandboxID, templateID, and the full
FailedToPlaceSandboxError message (node rejection counts + build CPU
constraints) so the cause is visible in log queries.
@leonmeijer

Copy link
Copy Markdown

This change is already incorporated on main by 063aa20, including typed and sanitized placement-failure reasons plus regression tests. Merging this branch would duplicate the current implementation, so this PR can be closed as superseded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

api: "Failed to place sandbox" gives no diagnostic - expose why no node accepted the sandbox

3 participants