{"record":{"id":"42ba16455ab1168d","repo":"gethomepage/homepage","slug":"widgets-yaml-must-contain-a-top-level-array","errorCode":null,"errorMessage":"widgets.yaml must contain a top-level array","messagePattern":"widgets\\.yaml must contain a top-level array","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/mcp/homepage-mcp.js","lineNumber":246,"sourceCode":"function addInfoWidget(args) {\n  const disabled = ensureWriteEnabled();\n  if (disabled) return disabled;\n\n  if (typeof args.type !== \"string\" || !args.type.trim()) {\n    throw new Error(\"type must be a non-empty string\");\n  }\n\n  const validation = validateYaml(\"widgets.yaml\", readConfig(\"widgets.yaml\"));\n  if (!validation.valid) {\n    return {\n      isError: true,\n      ...textContent(JSON.stringify(validation, null, 2)),\n    };\n  }\n\n  const widgets = parseYamlConfig(\"widgets.yaml\");\n  if (!Array.isArray(widgets)) {\n    throw new Error(\"widgets.yaml must contain a top-level array\");\n  }\n\n  const type = args.type.trim();\n  const options = args.options ?? {};\n  assertPlainObject(options, \"options\");\n\n  widgets.push({ [type]: options });\n  const content = dumpYamlConfig(\"widgets.yaml\", widgets);\n  return textContent(JSON.stringify({ written: \"widgets.yaml\", added: { type }, content }, null, 2));\n}\n\nfunction listConfigFiles() {\n  return CONFIG_FILES.map((file) => ({\n    file,\n    exists: fileExists(file),\n    writable: writeEnabled(),\n    description: FILE_DESCRIPTIONS[file],\n    docs: DOC_LINKS[file],","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/gethomepage/homepage/blob/b6dca1ae033e613d8e692f9a161a3cc53a5a2857/src/utils/mcp/homepage-mcp.js#L228-L264","documentation":"Thrown by addInfoWidget after validation passes when widgets.yaml is parsed and its top-level structure is not a YAML sequence. widgets.yaml must be a list of widget entries, so any other root shape is rejected as corruption.","triggerScenarios":"widgets.yaml exists and parseYamlConfig returns a non-array (YAML mapping, scalar, or null for a non-empty but non-list file). Mirrors the services.yaml top-level-array check.","commonSituations":"Hand-edit wrote widgets as a mapping (e.g. `resources: {}` at the root); an external tool converted the list to an object; an old/partial file has invalid YAML that parses to a scalar.","solutions":["Rewrite widgets.yaml so the root is a YAML sequence of single-key widget maps.","Start from the documented skeleton if unsure: `- resources: {}`.","Run validate_config_file on widgets.yaml before retrying.","Make the file empty (or `[]`) rather than `{}` if you want it blank."],"exampleFix":"# before (widgets.yaml)\nresources: {}\nsearch: {}\n\n# after\n- resources: {}\n- search: {}","handlingStrategy":"validation","validationCode":"function ensureWidgetsArray(parsed) {\n  if (!Array.isArray(parsed)) {\n    throw new Error('widgets.yaml root must be a YAML sequence; refusing to proceed');\n  }\n  return parsed;\n}","typeGuard":"function isWidgetsList(parsed) {\n  return Array.isArray(parsed);\n}","tryCatchPattern":null,"preventionTips":["Seed widgets.yaml from the documented template before enabling writes.","Run validate_config_file after manual edits.","Keep the root as a list even when empty ([]).","Document the expected shape for editors."],"tags":["mcp","widgets","yaml","configuration","validation"],"backgroundTag":null,"analyzedSha":"b6dca1ae033e613d8e692f9a161a3cc53a5a2857","analyzedAt":"2026-08-13T04:48:44.121Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}