{"record":{"id":"d963d372ac8640a7","repo":"redis/node-redis","slug":"invalid-json-configuration-error","errorCode":null,"errorMessage":"Invalid JSON configuration: ${error}","messagePattern":"Invalid JSON configuration: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/test-utils/lib/cae-client-testing.ts","lineNumber":16,"sourceCode":"import { readFile } from 'node:fs/promises';\n\ninterface RawRedisEndpoint {\n  username?: string;\n  password?: string;\n  tls: boolean;\n  endpoints: string[];\n}\n\nexport type RedisEndpointsConfig = Record<string, RawRedisEndpoint>;\n\nexport function loadFromJson(jsonString: string): RedisEndpointsConfig {\n  try {\n    return JSON.parse(jsonString) as RedisEndpointsConfig;\n  } catch (error) {\n    throw new Error(`Invalid JSON configuration: ${error}`);\n  }\n}\n\nexport async function loadFromFile(path: string): Promise<RedisEndpointsConfig> {\n  try {\n    const configFile = await readFile(path, 'utf-8');\n    return loadFromJson(configFile);\n  } catch (error) {\n    if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {\n      throw new Error(`Config file not found at path: ${path}`);\n    }\n    throw error;\n  }\n}","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/test-utils/lib/cae-client-testing.ts#L1-L30","documentation":"loadFromJson wraps JSON.parse and rethrows with this message when the supplied string is not valid JSON. It is used by the CAE test-utils endpoint loader to parse a RedisEndpointsConfig (a record of named endpoints). The underlying SyntaxError (including the token/position reported by the engine) is interpolated into the message.","triggerScenarios":"Calling loadFromJson(jsonString) with a string containing a JSON syntax error: trailing comma, single-quoted keys, unquoted strings, a BOM, or an empty string.","commonSituations":"Hand-editing a JSON config and introducing a typo; copy-pasting from a rich-text editor that converts quotes to smart quotes; passing a path string instead of file contents; encoding artifacts.","solutions":["Paste the JSON into a validator/linter to find the exact syntax location","Run JSON.parse(snippet) in isolation in a REPL to surface the precise position from the engine","Use loadFromFile(path) instead so you also get the clearer ENOENT path for missing files"],"exampleFix":"// before — trailing comma\n'{ \"tls\": false, \"endpoints\": [\"host:6379\",], }'\n\n// after — valid JSON\n'{ \"tls\": false, \"endpoints\": [\"host:6379\"] }'","handlingStrategy":"validation","validationCode":"function tryParseRedisConfig(jsonString: string): RedisEndpointsConfig {\n  try {\n    const parsed = JSON.parse(jsonString);\n    if (parsed === null || typeof parsed !== 'object') {\n      throw new Error('config must be a JSON object');\n    }\n    return parsed as RedisEndpointsConfig;\n  } catch (e) {\n    throw new Error(`Invalid JSON configuration: ${e instanceof Error ? e.message : e}`);\n  }\n}","typeGuard":"function isRedisEndpointsConfig(v: unknown): v is Record<string, RawRedisEndpoint> {\n  if (typeof v !== 'object' || v === null) return false;\n  for (const endpoint of Object.values(v)) {\n    if (typeof endpoint !== 'object' || endpoint === null) return false;\n    if (typeof endpoint.tls !== 'boolean') return false;\n    if (!Array.isArray(endpoint.endpoints) || !endpoint.endpoints.every((e: unknown) => typeof e === 'string')) return false;\n  }\n  return true;\n}","tryCatchPattern":"try {\n  const cfg = loadFromJson(raw);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Invalid JSON configuration')) {\n  }\n  throw e;\n}","preventionTips":["Lint config files with a JSON schema validator before loading","Prefer loadFromFile so file and parse errors are distinguishable","Wrap parse in a helper that returns a Result/throws a typed error instead of relying on message matching"],"tags":["json","configuration","parsing","test-utils"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}