Summary
createos sandbox pause and createos sandbox resume fail on every sandbox, addressed by name or by ID. The API returns HTTP 411 Length Required.
$ createos sandbox pause bench-2
ERROR Content-Length is required
Hint: Check that the value you provided is correct and try again.
Root cause
lifecyclePOST() in internal/api/sandbox.go builds the request with no .SetBody(...):
func (c *SandboxClient) lifecyclePOST(ctx context.Context, id, path string) (*SandboxView, error) {
var envelope Response[SandboxView]
resp, err := c.Client.R().
SetContext(ctx).
SetPathParam("id", id).
SetResult(&envelope).
Post(path)
...
Both PauseSandbox and ResumeSandbox call this helper, so both endpoints fail.
Over HTTP/2, a Go client request with no body sends no Content-Length header — it signals end of body with END_STREAM instead, which is valid HTTP/2. The API requires Content-Length on every POST and rejects the request with 411 before it reaches route logic. Other lifecycle calls that already set a body (CreateSandbox, ForkSandbox, AttachDisk, …) are unaffected.
Repro
$ createos --debug sandbox pause sb-01m0vyh91ayt2sardrn3b0vz8q
~~~ REQUEST ~~~
POST /v1/sandboxes/sb-01m0vyh91ayt2sardrn3b0vz8q/pause HTTP/1.1
BODY :
***** NO CONTENT *****
------------------------------------------------------------------------------
~~~ RESPONSE ~~~
STATUS : 411 Length Required
BODY :
{
"status": "fail",
"data": "Content-Length is required"
}
Identical failure for resume (POST /v1/sandboxes/{id}/resume).
Fix (verified against the live API)
Give the request a body:
resp, err := c.Client.R().
SetContext(ctx).
SetPathParam("id", id).
SetBody(map[string]any{}).
SetResult(&envelope).
Post(path)
Built and ran this exact change locally against api.sb.createos.sh:
pause returned 200 OK, sandbox reached paused.
resume returned 200 OK with status:"resuming", and the sandbox settled into running.
Version
Version: dev
Channel: stable
Commit: unknown
Built from main at b6f98bb ("fix(sandbox): pin ssh known_hosts to sandbox id (#76)").
Also noticed while restoring test state (separate, lower confidence)
After the fix, one resume briefly read back status:"paused" on the first poll or two before settling into resuming → running within ~11s. Might be read-path eventual consistency rather than a real regression — did not chase further, flagging in case it's already on your radar.
Summary
createos sandbox pauseandcreateos sandbox resumefail on every sandbox, addressed by name or by ID. The API returns HTTP 411 Length Required.Root cause
lifecyclePOST()ininternal/api/sandbox.gobuilds the request with no.SetBody(...):Both
PauseSandboxandResumeSandboxcall this helper, so both endpoints fail.Over HTTP/2, a Go client request with no body sends no
Content-Lengthheader — it signals end of body withEND_STREAMinstead, which is valid HTTP/2. The API requiresContent-Lengthon every POST and rejects the request with 411 before it reaches route logic. Other lifecycle calls that already set a body (CreateSandbox,ForkSandbox,AttachDisk, …) are unaffected.Repro
Identical failure for
resume(POST /v1/sandboxes/{id}/resume).Fix (verified against the live API)
Give the request a body:
Built and ran this exact change locally against
api.sb.createos.sh:pausereturned200 OK, sandbox reachedpaused.resumereturned200 OKwithstatus:"resuming", and the sandbox settled intorunning.Version
Built from
mainatb6f98bb("fix(sandbox): pin ssh known_hosts to sandbox id (#76)").Also noticed while restoring test state (separate, lower confidence)
After the fix, one
resumebriefly read backstatus:"paused"on the first poll or two before settling intoresuming→runningwithin ~11s. Might be read-path eventual consistency rather than a real regression — did not chase further, flagging in case it's already on your radar.