{"record":{"id":"960aca86244ac139","repo":"redis/node-redis","slug":"http-response-status","errorCode":null,"errorMessage":"HTTP ${response.status}","messagePattern":"HTTP \\$\\{response\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/test-utils/lib/fault-injector/fault-injector-client.ts","lineNumber":226,"sourceCode":"    const controller = new AbortController();\n    const timeoutId = globalThis.setTimeout(() => {\n      controller.abort();\n    }, timeoutMs);\n\n    try {\n      const response = await this.#fetch(url, {\n        method,\n        headers,\n        body: payload,\n        signal: controller.signal\n      });\n\n      if (!response.ok) {\n        try {\n          const text = await response.text();\n          throw new Error(`HTTP ${response.status} - ${text}`);\n        } catch {\n          throw new Error(`HTTP ${response.status}`);\n        }\n      }\n\n      try {\n        const result = (await response.json()) as T;\n        return result;\n      } catch {\n        throw new Error(\n          `HTTP ${response.status} - Unable to parse response as JSON`\n        );\n      }\n    } finally {\n      globalThis.clearTimeout(timeoutId);\n    }\n  }\n\n  /**\n   * Deletes a database.","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/test-utils/lib/fault-injector/fault-injector-client.ts#L208-L244","documentation":"Thrown by #request when the fault-injector HTTP response is not ok. Because of the try/catch shape in the source, this is the error callers actually receive for every non-ok response — it masks the richer error 112 (which includes the body). It fires both when response.text() fails and when the throw of error 112 is caught.","triggerScenarios":"Any 4xx/5xx from the fault-injector REST API (listActions, triggerAction, getActionStatus, createAndSelectDatabase, deleteDatabase). The caller sees only the status code, not the body.","commonSituations":"Wrong baseUrl or path; fault-injector service down; invalid action payload rejected with 400; 404 for an unknown action_id; 500 from a backend crash.","solutions":["Reproduce the exact request with curl against baseUrl to read the body the library hides","Fix the underlying status cause (404 → check the id; 400 → fix the payload; 5xx → check service logs)","Patch #request so the response body is surfaced (see error 112 exampleFix)"],"exampleFix":"# before — library hides the body\nError: HTTP 400\n\n# after — reproduce manually to see why\ncurl -i -X POST $FI_BASE_URL/action -H 'Content-Type: application/json' -d '<your-payload>'\n# then fix the request per the body, and/or patch the client","handlingStrategy":"try-catch","validationCode":"function buildFetchWithLogging(fetchImpl: typeof fetch): typeof fetch {\n  return async (input, init) => {\n    const res = await fetchImpl(input, init);\n    if (!res.ok) {\n      const clone = res.clone();\n      clone.text().then(t => console.error(`[FI] ${res.status} body:`, t)).catch(() => {});\n    }\n    return res;\n  };\n}\nconst fi = new FaultInjectorClient(baseUrl, buildFetchWithLogging(fetch));","typeGuard":"function isHttpError(e: unknown): e is Error {\n  return e instanceof Error && /HTTP \\d{3}/.test(e.message);\n}\nfunction httpStatusFromError(e: unknown): number | null {\n  const m = e instanceof Error ? e.message.match(/HTTP (\\d{3})/) : null;\n  return m ? Number(m[1]) : null;\n}","tryCatchPattern":"try {\n  await fi.triggerAction(action);\n} catch (e) {\n  const status = httpStatusFromError(e);\n  if (status && status >= 500) {\n  } else if (status && status >= 400) {\n  }\n  throw e;\n}","preventionTips":["Reproduce non-ok requests with curl -i to read the body the library discards","Confirm baseUrl is correct and the fault-injector service is reachable","Patch the client to surface response bodies (see error 112 exampleFix)"],"tags":["fault-injection","http","test-utils","error-handling"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}