gethomepage/homepage · error · Error

services.yaml must contain a top-level array

Error message

services.yaml must contain a top-level array

What it means

Thrown by addService after validation passes when services.yaml is parsed and its top-level structure is not a YAML sequence. Homepage's services config is fundamentally a list of group objects, so any other root shape is treated as corruption.

Source

Thrown at src/utils/mcp/homepage-mcp.js:196

  if (typeof args.group !== "string" || !args.group.trim()) {
    throw new Error("group must be a non-empty string");
  }
  if (typeof args.name !== "string" || !args.name.trim()) {
    throw new Error("name must be a non-empty string");
  }

  const validation = validateYaml("services.yaml", readConfig("services.yaml"));
  if (!validation.valid) {
    return {
      isError: true,
      ...textContent(JSON.stringify(validation, null, 2)),
    };
  }

  const services = parseYamlConfig("services.yaml");
  if (!Array.isArray(services)) {
    throw new Error("services.yaml must contain a top-level array");
  }

  const groupName = args.group.trim();
  const serviceName = args.name.trim();
  const serviceConfig = args.service ?? {};
  assertPlainObject(serviceConfig, "service");

  let group = services.find((entry) => isPlainObject(entry) && Object.keys(entry)[0] === groupName);
  if (!group) {
    group = { [groupName]: [] };
    services.push(group);
  }

  if (!Array.isArray(group[groupName])) {
    throw new Error(`Group '${groupName}' must contain an array`);
  }

  if (group[groupName].some((entry) => isPlainObject(entry) && Object.keys(entry)[0] === serviceName)) {

View on GitHub (pinned to b6dca1ae03)

Solutions

  1. Rewrite services.yaml so the root is a YAML sequence: a list whose entries are single-key group maps.
  2. Back up the current file, then start from the documented skeleton: `- GroupName: []`.
  3. Run validate_config_file on services.yaml to surface structural issues before re-running add_service.
  4. If the file should be empty, make it literally empty (or `[]`) rather than `{}`.

Example fix

# before (services.yaml)
Infra:
  - Grafana: { href: http://grafana:3000 }

# after
- Infra:
    - Grafana:
        href: http://grafana:3000
Defensive patterns

Strategy: validation

Validate before calling

function ensureServicesArray(parsed) {
  if (!Array.isArray(parsed)) {
    throw new Error('services.yaml root must be a YAML sequence; refusing to proceed');
  }
  return parsed;
}

Type guard

function isServicesList(parsed) {
  return Array.isArray(parsed) && parsed.every((e) => e != null && typeof e === 'object' && !Array.isArray(e));
}

Prevention

When it happens

Trigger: services.yaml exists, validates (or is empty enough to pass), and parseYamlConfig returns something whose `Array.isArray(...)` is false — e.g. a YAML mapping at the root, a scalar, or null when the file is non-empty but not a list.

Common situations: User hand-edited services.yaml into a mapping (e.g. `Infra: [...]` at the top level instead of `- Infra: [...]`); file contains only a comment then a non-list value; an external tool rewrote it as an object; partial/failed manual edit left invalid YAML that js-yaml still parsed as null/scalar.

Related errors


AI-assisted analysis of gethomepage/homepage@b6dca1ae03 (2026-08-13). Data as JSON: /api/errors/39b4133df7cb21d4. Report an issue: GitHub.