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

  1. Add a top-level `services:` mapping to the YAML with at least one service
  2. Fix indentation so each service is a named key under services (a mapping, not a list)
  3. Validate with `docker compose config` or a YAML linter before saving
  4. 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

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


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