{"record":{"id":"0603828a79e3ac38","repo":"cube-js/cube","slug":"response-status-401-unauthorized-request","errorCode":null,"errorMessage":"${response.status === 401 ? 'Unauthorized request' : 'Unexpected error'}","messagePattern":"\\$\\{response\\.status === 401 \\? 'Unauthorized request' : 'Unexpected error'\\}","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"packages/cubejs-pinot-driver/src/PinotDriver.ts","lineNumber":196,"sourceCode":"        'Content-Type': 'application/json',\n        ...this.authorizationHeaders()\n      }),\n      body: JSON.stringify({\n        sql: query,\n        queryOptions: `useMultistageEngine=true;enableNullHandling=${this.config.nullHandling};timeoutMs=${this.config.queryTimeout * 1000}`\n      })\n    });\n\n    let response: Response;\n\n    try {\n      response = await fetch(request);\n    } catch (error: any) {\n      throw toError(error);\n    }\n\n    if (!response.ok) {\n      throw toError({ message: response.status === 401 ? 'Unauthorized request' : 'Unexpected error' });\n    }\n\n    const result: PinotResponse = await response.json();\n\n    if (result?.exceptions?.length) {\n      throw toError(result.exceptions[0]);\n    }\n\n    return result;\n  }\n\n  public async queryPromised(query: string): Promise<any[] | StreamTableData> {\n    const { resultTable } = await this.request(query);\n    return this.normalizeResultOverColumns(resultTable.rows, resultTable.dataSchema.columnNames);\n  }\n\n  public async downloadQueryResults(\n    query: string,","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-pinot-driver/src/PinotDriver.ts#L178-L214","documentation":"The Pinot driver's `request()` performs a POST of the SQL query to the Pinot broker/controller. If the HTTP response is not ok, the driver throws a generic Error — 'Unauthorized request' for HTTP 401, or the opaque 'Unexpected error' for every other non-2xx status (403, 404, 500, 503, etc.). The thrown message discards the real status code and response body, so non-401 failures are hard to diagnose.","triggerScenarios":"Any Pinot SQL request (`queryPromised` / `downloadQueryResults`, reached via resultTable destructure) where `fetch` returns a non-ok status: 401 with a wrong/expired/missing authToken or basicAuth credentials, 403 from broker ACLs, 404 from a wrong broker URL/port, or 5xx when brokers are down or the query crashes the server.","commonSituations":"Misconfigured or expired Pinot auth token in cube.js config; wrong `basicAuth` user/password; broker URL pointing at controller instead of broker (or wrong port); Pinot cluster restarted/upgraded returning 503; TLS/proxy issues turning a healthy cluster into an erroring endpoint.","solutions":["If the message is 'Unauthorized request', verify the authToken (Bearer) or basicAuth user/password in the driver config and regenerate the token if expired.","Log `response.status` and the response body around the call (or curl the broker endpoint with the same headers/body) to see the real error for non-401 statuses.","Verify this.url points to the Pinot BROKER query endpoint (http://<broker>:8099/query/sql style) and is reachable from the Cube process.","Check Pinot broker health/logs for 5xx causes (broker down, table missing, query timeout) and retry once the cluster is healthy.","Patch/upgrade the driver to include response.status and body text in the thrown error for easier debugging."],"exampleFix":"// before\nif (!response.ok) {\n  throw toError({ message: response.status === 401 ? 'Unauthorized request' : 'Unexpected error' });\n}\n// after\nif (!response.ok) {\n  const body = await response.text().catch(() => '');\n  throw toError({ message: `Pinot request failed (${response.status}): ${body || response.statusText}` });\n}","handlingStrategy":"try-catch","validationCode":"function validatePinotConfig(config: { authToken?: string; basicAuth?: { user: string; password: string }; url: string }) {\n  if (!config.url || !/^https?:\\/\\//.test(config.url)) {\n    throw new Error('Pinot broker URL must be an absolute http(s) URL');\n  }\n  if (!config.authToken && !config.basicAuth) {\n    console.warn('No Pinot credentials configured; requests may return 401 Unauthorized');\n  }\n}","typeGuard":"function isUnauthorizedError(err: unknown): err is Error {\n  return err instanceof Error && err.message === 'Unauthorized request';\n}","tryCatchPattern":"try {\n  const rows = await driver.queryPromised(sql);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Unauthorized request') {\n    // refresh/rotate the Pinot authToken or basicAuth credentials, then retry once\n    await refreshCredentials();\n    return driver.queryPromised(sql);\n  }\n  if (err instanceof Error && err.message === 'Unexpected error') {\n    // inspect broker health/logs; opaque 4xx/5xx — log context and retry with backoff\n    await retryWithBackoff(() => driver.queryPromised(sql));\n    return;\n  }\n  throw err;\n}","preventionTips":["Configure a valid, unexpired authToken (Bearer) or basicAuth credentials for the Pinot cluster.","Point the driver URL at the Pinot broker query endpoint, not the controller, and confirm the port is reachable.","Pre-flight check the endpoint with curl using the same Authorization headers before deploying.","Handle token rotation: re-create or reconfigure the driver when credentials are refreshed.","Check Pinot broker health and logs when you see 'Unexpected error', since the driver hides the real status/body."],"tags":["http","authentication","pinot","network","api"],"backgroundTag":"http-401-unauthorized","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}