Skip to content

fix(stacks): pass environment id when loading saved compose for env validation#1168

Open
chris-ruecker wants to merge 1 commit into
Finsys:mainfrom
chris-ruecker:fix/env-validate-saved-compose-env-id
Open

fix(stacks): pass environment id when loading saved compose for env validation#1168
chris-ruecker wants to merge 1 commit into
Finsys:mainfrom
chris-ruecker:fix/env-validate-saved-compose-env-id

Conversation

@chris-ruecker

Copy link
Copy Markdown

Proposed change

POST /api/stacks/[name]/env/validate?env=<id> returns 400 {"error":"No compose content provided and no saved compose file found"} for every environment-scoped stack when the request body contains no compose content — even though the stack has a saved compose file and the endpoint documents this mode ("Can use saved compose file or accept compose content in body").

Root cause

The handler parses the environment id from the query string and uses it for the permission checks and the env var lookup, but drops it when loading the saved compose file:

const envId    = url.searchParams.get('env');
const envIdNum = envId ? parseInt(envId) : null;
// ...envIdNum is used for authorize() and getStackEnvVars()...
const savedCompose = await getStackComposeFile(stackName);   // ← env id missing

getStackComposeFile(stackName, envId?) resolves the stack via getStackSource(stackName, envId), and getStackSource treats a missing env id as isNull(stackSources.environmentId) — so for a stack with a non-null environmentId the lookup finds no row, getStackComposeFile returns needsFileLocation: true, and the handler responds 400.

Fix

Pass the already-parsed envIdNum through, matching what the sibling compose endpoint (src/routes/api/stacks/[name]/compose/+server.ts) already does:

- const savedCompose = await getStackComposeFile(stackName);
+ const savedCompose = await getStackComposeFile(stackName, envIdNum);

For non-env (legacy) stacks envIdNum is null, which preserves the current isNull(environmentId) lookup — their behaviour is unchanged.

Why this went unnoticed

The Dockhand UI (StackModal.svelte) always sends { compose, variables } in the request body, which bypasses the saved-file path entirely. Only API clients using the empty-body mode hit the bug. Discovered while writing end-to-end tests for a CLI client that calls this endpoint.

How to reproduce / test

Against any stack created in a non-default environment (replace <id> with the environment id):

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  "https://<host>/api/stacks/<stack>/env/validate?env=<id>"

Before: 400 {"error":"No compose content provided and no saved compose file found"}
After: 200 with the expected {valid, required, optional, defined, missing, unused} validation result.

Stacks without an environment scope and UI-driven validation (compose in body) behave exactly as before.

Type of change

  • Bug fix: non-breaking change which fixes an issue.
  • New feature / Enhancement: non-breaking change which adds functionality.
  • Breaking change: fix or feature that would cause existing functionality to not work as expected.
  • Other. Please explain:

@CLAassistant

CLAassistant commented Jun 12, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

…alidation

The env validate endpoint parses the env id from the query string and uses
it for permission checks and the env var lookup, but omitted it when loading
the stack's saved compose file. getStackSource then filters on a NULL
environmentId, so the lookup fails for every environment-scoped stack and
the endpoint returns 400 even though the compose file exists.

The dockhand UI always sends the compose content in the request body, so it
never hits this path; any API client relying on the documented saved-file
mode (empty body) does. The sibling compose endpoint already passes the env
id; this aligns the validate handler with it.
@chris-ruecker
chris-ruecker force-pushed the fix/env-validate-saved-compose-env-id branch from f66e3c0 to b9fc6f7 Compare June 17, 2026 18:10
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