{"record":{"id":"07d56f27c7f1c575","repo":"unionlabs/union","slug":"config-file-not-found-ensure-config-json-exists","errorCode":null,"errorMessage":"Config file not found. Ensure config.json exists.","messagePattern":"Config file not found\\. Ensure config\\.json exists\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"sentinel2/src/helpers.ts","lineNumber":76,"sourceCode":"  betterstack_api_key: string\n  trigger_betterstack: boolean\n  dbPath: string\n  isLocal: boolean\n}\n\nclass FilesystemError extends Data.TaggedError(\"FilesystemError\")<{\n  message: string\n  cause: unknown\n}> {}\n\nexport class Config extends Context.Tag(\"Config\")<Config, { readonly config: ConfigFile }>() {}\n\nexport function loadConfig(configPath: string) {\n  return Effect.tryPromise({\n    // biome-ignore lint/suspicious/useAwait: <explanation>\n    try: async () => {\n      if (!Fs.existsSync(configPath)) {\n        throw new Error(\"Config file not found. Ensure config.json exists.\")\n      }\n      const rawData = Fs.readFileSync(configPath, \"utf-8\")\n      const config: ConfigFile = JSON.parse(rawData)\n\n      return config\n    },\n    catch: error =>\n      new FilesystemError({\n        message: \"Config file is invalid.\",\n        cause: error,\n      }),\n  })\n}\n\nexport function hexToUtf8(hex: string): string {\n  // strip optional 0x\n  const clean = hex.startsWith(\"0x\") ? hex.slice(2) : hex\n  // build a Buffer from hex, then decode as UTF‑8","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/unionlabs/union/blob/031785bb6dc6b957c624e62bc64c184409c97d7b/sentinel2/src/helpers.ts#L58-L94","documentation":"sentinel2's loadConfig runs inside Effect.tryPromise: it checks Fs.existsSync(configPath) and throws this error when the config file is absent. The surrounding catch wraps ANY throw into FilesystemError('Config file is invalid.'), so operators usually see the misleading 'invalid' message and must dig into the cause chain to find the real 'not found' error.","triggerScenarios":"Running the sentinel binary/bun process from a working directory where config.json does not exist; passing a wrong config path argument; containers/CI where the config volume mount is missing.","commonSituations":"Relative configPath resolved against an unexpected cwd; config.json gitignored and never deployed; fresh clones running before creating the config.","solutions":["Create config.json at the exact path passed to loadConfig (or run from the directory that contains it)","Pass an absolute config path (env var or CLI arg) instead of relying on cwd","If you saw 'Config file is invalid.', inspect the FilesystemError's cause — it is often this not-found error, not a JSON problem","Fix the catch to distinguish missing file from parse failure so the message matches reality"],"exampleFix":"// before\ncatch: error =>\n  new FilesystemError({\n    message: \"Config file is invalid.\",\n    cause: error,\n  }),\n\n// after\ncatch: error =>\n  new FilesystemError({\n    message: Fs.existsSync(configPath)\n      ? \"Config file is invalid.\"\n      : `Config file not found at ${configPath}.`,\n    cause: error,\n  }),","handlingStrategy":"validation","validationCode":"import Fs from \"node:fs\"\n\nconst configPath = path.resolve(process.cwd(), \"config.json\")\nif (!Fs.existsSync(configPath)) {\n  console.error(`Missing ${configPath} — copy config.example.json and edit it`)\n  process.exit(1)\n}\nawait Effect.runPromise(loadConfig(configPath))","typeGuard":null,"tryCatchPattern":"import { Effect } from \"effect\"\n\nconst program = loadConfig(configPath).pipe(\n  Effect.catchTag(\"FilesystemError\", e => {\n    // e.cause holds the real error: missing file vs JSON.parse failure\n    console.error(e.message, e.cause)\n    return Effect.fail(e)\n  }),\n)","preventionTips":["Resolve config paths absolutely (CLI arg or env var), never rely on cwd","Ship a config.example.json and document the copy step","In containers, assert the config mount exists before starting the process"],"tags":["config","filesystem","effect","operations"],"backgroundTag":null,"analyzedSha":"031785bb6dc6b957c624e62bc64c184409c97d7b","analyzedAt":"2026-08-16T06:24:09.996Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}