{"record":{"id":"2a5b8a27271b4139","repo":"gethomepage/homepage","slug":"group-must-be-a-non-empty-string","errorCode":null,"errorMessage":"group must be a non-empty string","messagePattern":"group must be a non-empty string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/mcp/homepage-mcp.js","lineNumber":180,"sourceCode":"      ...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;\n}\n\nfunction addService(args) {\n  const disabled = ensureWriteEnabled();\n  if (disabled) return disabled;\n\n  if (typeof args.group !== \"string\" || !args.group.trim()) {\n    throw new Error(\"group must be a non-empty string\");\n  }\n  if (typeof args.name !== \"string\" || !args.name.trim()) {\n    throw new Error(\"name must be a non-empty string\");\n  }\n\n  const validation = validateYaml(\"services.yaml\", readConfig(\"services.yaml\"));\n  if (!validation.valid) {\n    return {\n      isError: true,\n      ...textContent(JSON.stringify(validation, null, 2)),\n    };\n  }\n\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","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/gethomepage/homepage/blob/b6dca1ae033e613d8e692f9a161a3cc53a5a2857/src/utils/mcp/homepage-mcp.js#L162-L198","documentation":"Thrown by addService when the `group` argument is missing or not a non-empty string. Each service in Homepage's services.yaml must live inside a named group, so the group identifier is mandatory.","triggerScenarios":"add_service tool called with args.group undefined, null, a number, an array, an empty string, or a whitespace-only string. The check is `typeof args.group !== \"string\" || !args.group.trim()`.","commonSituations":"MCP client omits group; passes an empty string; uses a numeric id; sends whitespace; confuses group with the service name.","solutions":["Provide group as a non-empty trimmed string, e.g. \"Infrastructure\".","If the group doesn't exist yet it will be created automatically — pick a clear human-readable name.","Check the tool-call arguments object actually contains a `group` key.","Avoid leading/trailing spaces; they're stripped but indicate a client bug."],"exampleFix":"// before\n{ \"name\": \"Grafana\", \"service\": { \"href\": \"http://grafana:3000\" } }\n\n// after\n{ \"group\": \"Monitoring\", \"name\": \"Grafana\", \"service\": { \"href\": \"http://grafana:3000\" } }","handlingStrategy":"validation","validationCode":"function isNonEmptyString(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}\nif (!isNonEmptyString(args.group)) {\n  return { isError: true, message: 'group is required' };\n}","typeGuard":"function isNonEmptyString(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Make group a required field in your MCP client schema.","Trim and validate identifiers before sending.","Reuse a single helper for all 'non-empty string' arg checks.","Return isError results instead of throwing when building MCP tool wrappers."],"tags":["mcp","services","validation","configuration"],"backgroundTag":null,"analyzedSha":"b6dca1ae033e613d8e692f9a161a3cc53a5a2857","analyzedAt":"2026-08-13T04:48:44.121Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}