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

  1. Send env as dotenv-style text ('KEY=value' lines), defaulting to '' when empty
  2. Join a key/value map before sending: Object.entries(map).map(([k,v]) => `${k}=${v}`).join('\n')
  3. Initialize the env editor with an empty string, never undefined
  4. 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

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


AI-assisted analysis of louislam/dockge@f809ae192b (2026-08-31). Data as JSON: /api/errors/1a657e3738609e00. Report an issue: GitHub.