unionlabs/union · error · Error
Config file not found. Ensure config.json exists.
Error message
Config file not found. Ensure config.json exists.
What it means
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.
Source
Thrown at sentinel2/src/helpers.ts:76
betterstack_api_key: string
trigger_betterstack: boolean
dbPath: string
isLocal: boolean
}
class FilesystemError extends Data.TaggedError("FilesystemError")<{
message: string
cause: unknown
}> {}
export class Config extends Context.Tag("Config")<Config, { readonly config: ConfigFile }>() {}
export function loadConfig(configPath: string) {
return Effect.tryPromise({
// biome-ignore lint/suspicious/useAwait: <explanation>
try: async () => {
if (!Fs.existsSync(configPath)) {
throw new Error("Config file not found. Ensure config.json exists.")
}
const rawData = Fs.readFileSync(configPath, "utf-8")
const config: ConfigFile = JSON.parse(rawData)
return config
},
catch: error =>
new FilesystemError({
message: "Config file is invalid.",
cause: error,
}),
})
}
export function hexToUtf8(hex: string): string {
// strip optional 0x
const clean = hex.startsWith("0x") ? hex.slice(2) : hex
// build a Buffer from hex, then decode as UTF‑8View on GitHub (pinned to 031785bb6d)
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
Example fix
// before
catch: error =>
new FilesystemError({
message: "Config file is invalid.",
cause: error,
}),
// after
catch: error =>
new FilesystemError({
message: Fs.existsSync(configPath)
? "Config file is invalid."
: `Config file not found at ${configPath}.`,
cause: error,
}), Defensive patterns
Strategy: validation
Validate before calling
import Fs from "node:fs"
const configPath = path.resolve(process.cwd(), "config.json")
if (!Fs.existsSync(configPath)) {
console.error(`Missing ${configPath} — copy config.example.json and edit it`)
process.exit(1)
}
await Effect.runPromise(loadConfig(configPath)) Try / catch
import { Effect } from "effect"
const program = loadConfig(configPath).pipe(
Effect.catchTag("FilesystemError", e => {
// e.cause holds the real error: missing file vs JSON.parse failure
console.error(e.message, e.cause)
return Effect.fail(e)
}),
) Prevention
- 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
When it happens
Trigger: 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.
Common situations: Relative configPath resolved against an unexpected cwd; config.json gitignored and never deployed; fresh clones running before creating the config.
Related errors
AI-assisted analysis of unionlabs/union@031785bb6d (2026-08-16).
Data as JSON: /api/errors/07d56f27c7f1c575.
Report an issue: GitHub.