louislam/dockge · error · ValidationError
Compose ENV must be a string
Error message
Compose ENV must be a string
What it means
ValidationError from saveStack: the composeENV argument (contents of the stack's .env file) must be a string. It is written to disk as-is, so undefined/null/objects are rejected before Stack creation and save.
Source
Thrown at backend/agent-socket-handlers/docker-socket-handler.ts:345
ok: true,
dockerNetworkList,
}, callback);
} catch (e) {
callbackError(e, callback);
}
});
}
async saveStack(server : DockgeServer, name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown) : Promise<Stack> {
// Check types
if (typeof(name) !== "string") {
throw new ValidationError("Name must be a string");
}
if (typeof(composeYAML) !== "string") {
throw new ValidationError("Compose YAML must be a string");
}
if (typeof(composeENV) !== "string") {
throw new ValidationError("Compose ENV must be a string");
}
if (typeof(isAdd) !== "boolean") {
throw new ValidationError("isAdd must be a boolean");
}
const stack = new Stack(server, name, composeYAML, composeENV, false);
await stack.save(isAdd);
return stack;
}
}
View on GitHub (pinned to f809ae192b)
Solutions
- Send env as dotenv-style text ('KEY=value' lines), defaulting to '' when empty
- Join a key/value map before sending: Object.entries(map).map(([k,v]) => `${k}=${v}`).join('\n')
- Initialize the env editor with an empty string, never undefined
- Validate typeof env === 'string' before emit
Example fix
// before
socket.emit("create", name, yaml, envMap, isAdd, cb); // object
// after
const envStr = Object.entries(envMap).map(([k, v]) => `${k}=${v}`).join("\n");
socket.emit("create", name, yaml, envStr, isAdd, cb); Defensive patterns
Strategy: validation
Validate before calling
function toEnvString(env: unknown): string {
if (typeof env === "string") return env;
if (env && typeof env === "object") {
return Object.entries(env).map(([k, v]) => `${k}=${v}`).join("\n");
}
return ""; // undefined/null -> empty .env
} Type guard
const isEnvString = (v: unknown): v is string => typeof v === "string";
Try / catch
try {
const envStr = toEnvString(rawEnv);
socket.emit("create", name, yaml, envStr, isAdd, cb);
} catch (e) { showError((e as Error).message); } Prevention
- Represent .env as dotenv-format text end to end
- Default the env editor to an empty string on mount
- Convert key/value maps to 'KEY=value' lines before emitting
When it happens
Trigger: Emitting 'create'/'stack' with composeENV undefined (env editor never initialized), null after a failed env fetch, or an object (key/value map sent instead of dotenv-format text).
Common situations: New stacks where the env pane has no value yet; passing a parsed env object like { KEY: 'value' } instead of 'KEY=value' lines; frontend state reset losing the env string.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- Stack name and service name must be strings
- Invalid stackName or serviceName
- Compose YAML must be a string
- isAdd must be a boolean
- Terminal name must be a string.
AI-assisted analysis of louislam/dockge@f809ae192b (2026-08-31).
Data as JSON: /api/errors/1a657e3738609e00.
Report an issue: GitHub.