{"record":{"id":"685c2bc6dfe956ac","repo":"affaan-m/ECC","slug":"unable-to-read-label-at-filepath-error-message","errorCode":null,"errorMessage":"Unable to read ${label} at ${filePath}: ${error.message}","messagePattern":"Unable to read (.+?) at (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/hooks-config.js","lineNumber":32,"sourceCode":" * so every sidecar entry also carries a fingerprint of the matcher entry it\n * describes. A mismatch means the two files drifted apart.\n */\n\nconst crypto = require('crypto');\nconst fs = require('fs');\nconst path = require('path');\n\nconst HOOKS_FILENAME = 'hooks.json';\nconst METADATA_FILENAME = 'hooks.metadata.json';\nconst FINGERPRINT_LENGTH = 12;\nconst FINGERPRINT_PATTERN = /^[0-9a-f]{12}$/;\n\nfunction readJsonObject(filePath, label) {\n  let raw;\n  try {\n    raw = fs.readFileSync(filePath, 'utf8');\n  } catch (error) {\n    throw new Error(`Unable to read ${label} at ${filePath}: ${error.message}`);\n  }\n\n  let parsed;\n  try {\n    parsed = JSON.parse(raw);\n  } catch (error) {\n    throw new Error(`Invalid JSON in ${label} at ${filePath}: ${error.message}`);\n  }\n\n  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n    throw new Error(`Invalid ${label} at ${filePath}: expected a JSON object`);\n  }\n\n  return parsed;\n}\n\nfunction metadataPathFor(hooksPath) {\n  return path.join(path.dirname(hooksPath), METADATA_FILENAME);","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/hooks-config.js#L14-L50","documentation":"readJsonObject() in scripts/lib/hooks-config.js reads a JSON file from disk (labelled, e.g. settings.json or the hooks metadata sidecar). If fs.readFileSync throws — file missing, permission denied, path is a directory — it rethrows as 'Unable to read <label> at <filePath>: <reason>'. This gives callers a consistent error naming the file and the underlying OS reason.","triggerScenarios":"Calling hooksConfig(), readHooksConfig(), or addJsonMergeOperation() where the hooks/settings JSON path does not exist, is unreadable due to permissions, is a directory, or sits on an unmounted volume.","commonSituations":"Running ECC hooks tooling before the project's settings.json exists; wrong working directory so relative paths resolve elsewhere; restrictive file permissions after cloning as another user; ENOENT on the metadata sidecar during first-time setup.","solutions":["Create the file at <filePath> (e.g. run the setup step that initializes hooks config) or point the tool at an existing file.","Fix file permissions so the current user can read it (chmod/chown).","Verify the path is a file, not a directory, and that you are running from the expected project root.","Read error.message in the thrown error for the exact OS reason (ENOENT vs EACCES vs EISDIR)."],"exampleFix":"// before\nconst cfg = readHooksConfig('.claude/settings.json'); // ENOENT if missing\n// after\nconst fs = require('fs');\nconst path = '.claude/settings.json';\nif (!fs.existsSync(path)) fs.writeFileSync(path, '{}');\nconst cfg = readHooksConfig(path);","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nfunction canReadFile(p) {\n  try { return fs.statSync(p).isFile() && fs.accessSync(p, fs.constants.R_OK) === undefined; }\n  catch { return false; }\n}\nif (!canReadFile('.claude/settings.json')) {\n  fs.writeFileSync('.claude/settings.json', '{}'); // create default before reading\n}","typeGuard":"function isReadableFile(p) { try { return fs.statSync(p).isFile(); } catch { return false; } }","tryCatchPattern":"try {\n  const cfg = readHooksConfig(path);\n} catch (err) {\n  if (String(err.message).startsWith('Unable to read')) {\n    console.error(`Cannot read ${path}: ${err.message}. Creating default.`);\n    fs.writeFileSync(path, '{}');\n  } else throw err;\n}","preventionTips":["Run ECC tooling from the project root so relative paths resolve correctly.","Create default config files during setup before any tool reads them.","Check file permissions after cloning or switching users.","Match on the 'Unable to read' prefix to distinguish read failures from parse failures."],"tags":["filesystem","json-config","file-read","hooks"],"backgroundTag":"file-read-failed","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"}