{"record":{"id":"ee156496952b0621","repo":"affaan-m/ECC","slug":"label-is-invalid-at-filepath-error-messag","errorCode":null,"errorMessage":"${label} is invalid at ${filePath}: ${error.message}","messagePattern":"(.+?) is invalid at (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/inventory.js","lineNumber":36,"sourceCode":"    || os.homedir();\n  const configDir = options.configDir\n    || process.env.CLAUDE_CONFIG_DIR\n    || path.join(homeDir, '.claude');\n  const projectRoot = options.projectRoot || process.cwd();\n\n  return {\n    homeDir: path.resolve(homeDir),\n    configDir: path.resolve(configDir),\n    projectRoot: path.resolve(projectRoot),\n  };\n}\n\nfunction readJsonObject(filePath, label) {\n  let value;\n  try {\n    value = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n  } catch (error) {\n    throw new Error(`${label} is invalid at ${filePath}: ${error.message}`);\n  }\n  if (!value || typeof value !== 'object' || Array.isArray(value)) {\n    throw new Error(`${label} is invalid at ${filePath}: expected a JSON object`);\n  }\n  return value;\n}\n\nfunction findManualClaudePlugin(options = {}) {\n  const { configDir } = resolveClaudePaths(options);\n  const pluginsDir = path.join(configDir, 'plugins');\n  const candidates = [\n    ['ecc', '.claude-plugin', 'plugin.json'],\n    ['ecc', 'plugin.json'],\n    ['ecc@ecc', '.claude-plugin', 'plugin.json'],\n    ['ecc@ecc', 'plugin.json'],\n    ['everything-claude-code', '.claude-plugin', 'plugin.json'],\n    ['everything-claude-code', 'plugin.json'],\n  ];","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/inventory.js#L18-L54","documentation":"readJsonObject() in inventory.js parses a JSON file from disk. If JSON.parse or the read fails, it wraps the underlying error message into this error carrying the label, file path, and parse/read failure reason. It distinguishes malformed/unreadable JSON from JSON that is merely the wrong top-level type.","triggerScenarios":"Calling readJsonObject(filePath, label) on a file containing invalid JSON (trailing commas, comments, truncation); the file cannot be read (missing file, permission error) — those throw the same wrapped message with the underlying error.message; a partially written state file from an interrupted process.","commonSituations":"Hand-editing Claude install state or inventory JSON and breaking syntax; a crashed installer leaving a truncated file; the file being replaced by a symlink, empty file, or lockfile artifact; encoding issues (BOM) breaking JSON.parse.","solutions":["Fix the JSON syntax at the path shown — run the file through a JSON linter or `node -e \"JSON.parse(require('fs').readFileSync(p,'utf8'))\"` to see the exact parse error.","Restore the file from version control or a backup if it was hand-edited or corrupted.","If the file is unrecoverable generated state, delete it and let the installer recreate it via a fresh install.","Check file permissions/ownership if the message wraps a read/permission error rather than a parse error."],"exampleFix":"// before (state.json)\n{ \"selectedModules\": [\"agents\"], }  // trailing comma\n// after\n{ \"selectedModules\": [\"agents\"] }","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nfunction isParseableObject(p) {\n  try {\n    JSON.parse(fs.readFileSync(p, 'utf8'));\n    return true;\n  } catch {\n    return false;\n  }\n}","typeGuard":"function isPlainObject(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  const state = readJsonObject(statePath, 'Managed Claude install-state');\n} catch (err) {\n  if (err.message.includes('is invalid at')) {\n    console.error(`${err.message} — restore from backup or re-run install`);\n    process.exitCode = 1;\n  } else throw err;\n}","preventionTips":["Never hand-edit generated JSON state files with editors that add comments/trailing commas.","Validate JSON after any manual change with a parser before running installer commands.","Avoid killing the installer mid-write; use atomic writes or wait for completion.","Keep state files in backups so corrupted files are cheap to restore."],"tags":["json","parse-error","inventory"],"backgroundTag":"json-parse-error","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}