louislam/dockge · error · Error
Services must be an object
Error message
Services must be an object
What it means
In frontend/src/pages/Compose.vue, when parsing the compose YAML the code ensures `config.services` exists and then validates that it is a non-array object. If yaml-js parses `services:` as something other than a mapping (an array, a string, a number) or it is missing entirely in a way that can't be defaulted, the parser throws 'Services must be an object'. This enforces the Docker Compose schema requirement that services is a mapping of service name -> definition.
Source
Thrown at frontend/src/pages/Compose.vue:735
this.isEditMode = false;
},
yamlToJSON(yaml) {
let doc = parseDocument(yaml);
if (doc.errors.length > 0) {
throw doc.errors[0];
}
const config = doc.toJS() ?? {};
// Check data types
// "services" must be an object
if (!config.services) {
config.services = {};
}
if (Array.isArray(config.services) || typeof config.services !== "object") {
throw new Error("Services must be an object");
}
return {
config,
doc,
};
},
yamlCodeChange() {
try {
let { config, doc } = this.yamlToJSON(this.stack.composeYAML);
this.yamlDoc = doc;
this.jsonConfig = config;
let env = dotenv.parse(this.stack.composeENV);
let envYAML = envsubstYAML(this.stack.composeYAML, env);
this.envsubstJSONConfig = this.yamlToJSON(envYAML).config;View on GitHub (pinned to f809ae192b)
Solutions
- Add a top-level `services:` mapping to the YAML with at least one service
- Fix indentation so each service is a named key under services (a mapping, not a list)
- Validate with `docker compose config` or a YAML linter before saving
- If services is intentionally empty, write `services: {}` explicitly
Example fix
// before (invalid)
services:
- web
image: nginx
// after
services:
web:
image: nginx Defensive patterns
Strategy: validation
Validate before calling
const doc = yaml.load(editorValue);
if (!doc || typeof doc.services !== "object" || Array.isArray(doc.services)) {
showError("YAML must contain a top-level 'services' mapping.");
return;
} Type guard
function hasValidServices(doc) {
return doc !== null && typeof doc === "object"
&& typeof doc.services === "object" && doc.services !== null
&& !Array.isArray(doc.services);
} Try / catch
try {
const { config, doc } = parseCompose(editorValue);
saveStack(config);
} catch (e) {
if (e.message === "Services must be an object") {
showError("Add a top-level services: mapping to your compose YAML.");
} else throw e;
} Prevention
- Keep `services:` as a top-level mapping with correct 2-space indentation
- Validate with `docker compose config` or a YAML linter before saving
- Use `services: {}` if a stack temporarily has no services
- Avoid pasting non-compose YAML into the stack editor
When it happens
Trigger: Editing the compose editor content so that `services:` is absent, or written as a list (`services: - web`), or as a scalar (`services: web`), then triggering save/parse of the stack.
Common situations: Hand-edited YAML with wrong indentation causing services to parse as something unexpected; users pasting a `docker run` style file or plain YAML without a services key; accidentally deleting the services block while editing.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Compose YAML must be a string
- Please enter a docker run command
- Name must be a string
- Stack name must be a string
- Stack name and service name must be strings
AI-assisted analysis of louislam/dockge@f809ae192b (2026-08-31).
Data as JSON: /api/errors/a66dc5a71e0122f0.
Report an issue: GitHub.