{"record":{"id":"2489167b15b30733","repo":"gethomepage/homepage","slug":"group-groupname-must-contain-an-array","errorCode":null,"errorMessage":"Group '${groupName}' must contain an array","messagePattern":"Group '(.+?)' must contain an array","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/mcp/homepage-mcp.js","lineNumber":211,"sourceCode":"\n  const services = parseYamlConfig(\"services.yaml\");\n  if (!Array.isArray(services)) {\n    throw new Error(\"services.yaml must contain a top-level array\");\n  }\n\n  const groupName = args.group.trim();\n  const serviceName = args.name.trim();\n  const serviceConfig = args.service ?? {};\n  assertPlainObject(serviceConfig, \"service\");\n\n  let group = services.find((entry) => isPlainObject(entry) && Object.keys(entry)[0] === groupName);\n  if (!group) {\n    group = { [groupName]: [] };\n    services.push(group);\n  }\n\n  if (!Array.isArray(group[groupName])) {\n    throw new Error(`Group '${groupName}' must contain an array`);\n  }\n\n  if (group[groupName].some((entry) => isPlainObject(entry) && Object.keys(entry)[0] === serviceName)) {\n    return {\n      isError: true,\n      ...textContent(`Service '${serviceName}' already exists in group '${groupName}'.`),\n    };\n  }\n\n  group[groupName].push({ [serviceName]: serviceConfig });\n  const content = dumpYamlConfig(\"services.yaml\", services);\n  return textContent(\n    JSON.stringify({ written: \"services.yaml\", added: { group: groupName, service: serviceName }, content }, null, 2),\n  );\n}\n\nfunction addInfoWidget(args) {\n  const disabled = ensureWriteEnabled();","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/gethomepage/homepage/blob/b6dca1ae033e613d8e692f9a161a3cc53a5a2857/src/utils/mcp/homepage-mcp.js#L193-L229","documentation":"Thrown by addService when an existing group is found but the value attached to that group key is not an array. Homepage expects each group to map to a list of service entries; a group mapping to a scalar/object blocks insertion.","triggerScenarios":"addService locates the group via `services.find(...)` and then checks `Array.isArray(group[groupName])`. It throws if, for example, services.yaml contains `- Infra: {}` or `- Infra: \"some string\"` instead of `- Infra: []`.","commonSituations":"Hand-edit produced `- Group: {}` or `- Group: null`; an older export wrote group metadata instead of a service list; partial migration from another dashboard format.","solutions":["Open services.yaml and change the malformed group's value to a YAML sequence (e.g. `- Infra: []`).","Use read_config_file / validate_config_file to inspect current shape before retrying add_service.","Move any legitimate group-level metadata elsewhere — Homepage stores only the service list under the group key.","Retry the add_service call once the structure is corrected."],"exampleFix":"# before (services.yaml)\n- Infra: {}\n\n# after\n- Infra:\n    - Grafana:\n        href: http://grafana:3000","handlingStrategy":"validation","validationCode":"function findOrCreateGroup(services, groupName) {\n  let g = services.find((e) => isPlainObject(e) && Object.keys(e)[0] === groupName);\n  if (!g) { g = { [groupName]: [] }; services.push(g); }\n  if (!Array.isArray(g[groupName])) throw new Error(`Group '${groupName}' must contain an array`);\n  return g;\n}","typeGuard":"function groupHasArray(entry, groupName) {\n  return isPlainObject(entry) && Array.isArray(entry[groupName]);\n}","tryCatchPattern":null,"preventionTips":["Normalize group values to [] before writing programmatically.","Reject and report malformed groups rather than coercing them.","Validate after every manual edit.","Document the expected group shape for editors."],"tags":["mcp","services","yaml","configuration","validation"],"backgroundTag":null,"analyzedSha":"b6dca1ae033e613d8e692f9a161a3cc53a5a2857","analyzedAt":"2026-08-13T04:48:44.121Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}