fix(api): GetSandboxesSandboxID omits VolumeMounts and sets empty string Alias for paused sandboxes
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 1.6k
- Forks
- 438
- PR merge metrics
- No merged PRs in 30d
Description
Problem
When querying sandbox metadata via GET /sandboxes/{sandboxID} (packages/api/internal/handlers/sandbox_get.go), paused or stopped sandboxes loaded from lastSnapshot exhibit two payload inconsistencies with the OpenAPI specification and list endpoints:
-
Missing
VolumeMountsin Paused Sandbox Response:
When a sandbox is paused,lastSnapshot.Snapshot.Config.VolumeMountscontains all persistent volume mount mappings. However,GetSandboxesSandboxIDconstructsapi.SandboxDetailwithout populating theVolumeMountsfield, resulting in"volume_mounts": nullor omission in the JSON response. In contrast, the list endpointGET /v2/sandboxes(packages/api/internal/handlers/sandboxes_list.go:L292) correctly mapsVolumeMountsfor paused sandboxes viaconvertFromDBMountsToAPIMounts. -
Malformed
AliasSerialized as""(Empty String) instead ofnull/ Omission:
When a paused sandbox has no aliases,pausedAlias := firstAlias(lastSnapshot.Aliases)returns""(empty string).sandbox.Alias = &pausedAliasthen assigns a non-nil pointer to"". When serialized to JSON, this emits"alias": ""instead ofnullor omitting the property, violating the OpenAPI schema contract for optional string fields. -
Potential Nil Pointer Panic on
a.orchestrator:
GetSandboxesSandboxIDattempts to calla.orchestrator.GetSandboxwithout verifyinga.orchestrator != nil, causing panics in isolated unit tests and environments where the orchestrator client is nil.
Root Cause
In packages/api/internal/handlers/sandbox_get.go:L235-L265:
// Current implementation for paused sandboxes:
pausedAlias := firstAlias(lastSnapshot.Aliases)
sandbox = api.SandboxDetail{
// VolumeMounts is omitted completely
Alias: &pausedAlias, // Points to "" when len(Aliases) == 0
...
}
Comparison of metadata mapping across endpoints:
| Endpoint | Paused Sandbox VolumeMounts |
Empty Alias Representation |
|---|---|---|
GET /v2/sandboxes (sandboxes_list.go) |
Populated via convertFromDBMountsToAPIMounts |
nil (omitted) |
GET /sandboxes/{id} (Current) |
nil (omitted) |
&"" (serialized as "") |
GET /sandboxes/{id} (Expected) |
Populated from Config.VolumeMounts |
nil (omitted) |
Reproduction Steps
- Create a sandbox with persistent volume mounts:
POST /sandboxeswithvolume_mounts: [{"name": "my-vol", "path": "/mnt/data"}]. - Pause the sandbox:
POST /sandboxes/{id}/pause. - Query the paused sandbox:
GET /sandboxes/{id}. - Observed:
volume_mountsisnullor missing from the JSON payload.aliasis""(empty string) if no alias was assigned.
- Expected:
volume_mountscontains[{"name": "my-vol", "path": "/mnt/data"}].aliasisnullor omitted when no alias exists.
Technical Context
- File affected:
packages/api/internal/handlers/sandbox_get.go - Subsystem: Control Plane API / Sandboxes
- Impact: Medium (API Schema compliance, client SDK consistency, and paused sandbox metadata fidelity)
Proposed Changes
| # | Change | File(s) Affected | Complexity |
|---|---|---|---|
| 1 | Add nil check on a.orchestrator != nil before calling GetSandbox |
sandbox_get.go |
Trivial |
| 2 | Extract volumeMounts from lastSnapshot.Snapshot.Config.VolumeMounts and pass to api.SandboxDetail |
sandbox_get.go |
Low |
| 3 | Populate sandbox.Alias only when len(lastSnapshot.Aliases) > 0, leaving it nil otherwise |
sandbox_get.go |
Trivial |
| 4 | Add unit test asserting VolumeMounts and nil alias on paused snapshot |
sandbox_get_test.go |
Low |
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/api/internal/handlers/sandbox_get.go, especially the paused-sandbox mapping around lines 235-265, and compare its VolumeMounts and Alias handling with sandboxes_list.go. Add the orchestrator nil guard and cover the paused snapshot case in sandbox_get_test.go. Done means paused responses preserve volume mounts, omit an empty alias, and do not panic with a nil orchestrator.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100