{"record":{"id":"2575e29722b25861","repo":"gethomepage/homepage","slug":"name-must-be-an-object","errorCode":null,"errorMessage":"${name} must be an object","messagePattern":"(.+?) must be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/mcp/homepage-mcp.js","lineNumber":154,"sourceCode":"      error: error.message,\n      mark: error.mark\n        ? {\n            line: error.mark.line + 1,\n            column: error.mark.column + 1,\n            snippet: error.mark.snippet,\n          }\n        : undefined,\n    };\n  }\n}\n\nfunction isPlainObject(value) {\n  return value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction assertPlainObject(value, name) {\n  if (!isPlainObject(value)) {\n    throw new Error(`${name} must be an object`);\n  }\n}\n\nfunction ensureWriteEnabled() {\n  if (!writeEnabled()) {\n    return {\n      isError: true,\n      ...textContent(\"Writing is disabled. Set HOMEPAGE_MCP_ALLOW_WRITE=true to enable MCP config edits.\"),\n    };\n  }\n  return null;\n}\n\nfunction dumpYamlConfig(file, content) {\n  const dumped = yaml.dump(content, { lineWidth: -1, noRefs: true });\n  mkdirSync(CONF_DIR, { recursive: true });\n  writeFileSync(configPath(file), dumped, \"utf8\");\n  return dumped;","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/gethomepage/homepage/blob/b6dca1ae033e613d8e692f9a161a3cc53a5a2857/src/utils/mcp/homepage-mcp.js#L136-L172","documentation":"Thrown by assertPlainObject() when a value the caller supplied (or that was read from YAML) is expected to be a plain object but is an array, primitive, null, or non-object. Used by addService/addInfoWidget/write helpers to keep YAML structure well-formed.","triggerScenarios":"assertPlainObject(value, name) called with a non-object. Concrete call sites: addService passes args.service (e.g. caller sent `\"service\": []` or `\"service\": \"text\"`); addInfoWidget passes args.options; both also pass the parsed `name` through. The check is `!isPlainObject(value)` where isPlainObject excludes arrays and null.","commonSituations":"MCP client sends a JSON array where an object was expected (service config as a list of strings); client omits the field and a default isn't supplied; an existing YAML file already has the wrong shape (e.g. a service entry is a string instead of a map).","solutions":["Pass a JSON object (not array, not string) for the indicated field — e.g. `\"service\": { \"href\": \"...\" }`.","If the field is optional, omit it entirely so the `?? {}` default applies rather than sending null.","Inspect the existing YAML file the tool is operating on — a malformed entry there can also trip this on read.","Validate payload shape against the tool's input schema before sending."],"exampleFix":"// before\n{ \"group\": \"Infra\", \"name\": \"Grafana\", \"service\": [\"http://grafana:3000\"] }\n\n// after\n{ \"group\": \"Infra\", \"name\": \"Grafana\", \"service\": { \"href\": \"http://grafana:3000\", \"description\": \"Dashboards\" } }","handlingStrategy":"type-guard","validationCode":"function isPlainObject(v) {\n  return v != null && typeof v === 'object' && !Array.isArray(v);\n}\n// before add_service:\nif (!isPlainObject(args.service)) args.service = {};","typeGuard":"function isPlainObject(v) {\n  return v != null && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["Send JSON objects (not arrays) for nested config fields.","Let optional fields be omitted rather than sending null.","Validate the request payload against the tool schema before sending.","Inspect existing YAML files for shape drift before mutating them."],"tags":["mcp","validation","configuration","types"],"backgroundTag":null,"analyzedSha":"b6dca1ae033e613d8e692f9a161a3cc53a5a2857","analyzedAt":"2026-08-13T04:48:44.121Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}