cube-js/cube · error

${response.status === 401 ? 'Unauthorized request' : 'Unexpe

Error message

${response.status === 401 ? 'Unauthorized request' : 'Unexpected error'}

What it means

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.

Source

Thrown at packages/cubejs-pinot-driver/src/PinotDriver.ts:196

        'Content-Type': 'application/json',
        ...this.authorizationHeaders()
      }),
      body: JSON.stringify({
        sql: query,
        queryOptions: `useMultistageEngine=true;enableNullHandling=${this.config.nullHandling};timeoutMs=${this.config.queryTimeout * 1000}`
      })
    });

    let response: Response;

    try {
      response = await fetch(request);
    } catch (error: any) {
      throw toError(error);
    }

    if (!response.ok) {
      throw toError({ message: response.status === 401 ? 'Unauthorized request' : 'Unexpected error' });
    }

    const result: PinotResponse = await response.json();

    if (result?.exceptions?.length) {
      throw toError(result.exceptions[0]);
    }

    return result;
  }

  public async queryPromised(query: string): Promise<any[] | StreamTableData> {
    const { resultTable } = await this.request(query);
    return this.normalizeResultOverColumns(resultTable.rows, resultTable.dataSchema.columnNames);
  }

  public async downloadQueryResults(
    query: string,

View on GitHub (pinned to 7d981676b3)

Solutions

  1. If the message is 'Unauthorized request', verify the authToken (Bearer) or basicAuth user/password in the driver config and regenerate the token if expired.
  2. 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.
  3. Verify this.url points to the Pinot BROKER query endpoint (http://<broker>:8099/query/sql style) and is reachable from the Cube process.
  4. Check Pinot broker health/logs for 5xx causes (broker down, table missing, query timeout) and retry once the cluster is healthy.
  5. Patch/upgrade the driver to include response.status and body text in the thrown error for easier debugging.

Example fix

// before
if (!response.ok) {
  throw toError({ message: response.status === 401 ? 'Unauthorized request' : 'Unexpected error' });
}
// after
if (!response.ok) {
  const body = await response.text().catch(() => '');
  throw toError({ message: `Pinot request failed (${response.status}): ${body || response.statusText}` });
}
Defensive patterns

Strategy: try-catch

Validate before calling

function validatePinotConfig(config: { authToken?: string; basicAuth?: { user: string; password: string }; url: string }) {
  if (!config.url || !/^https?:\/\//.test(config.url)) {
    throw new Error('Pinot broker URL must be an absolute http(s) URL');
  }
  if (!config.authToken && !config.basicAuth) {
    console.warn('No Pinot credentials configured; requests may return 401 Unauthorized');
  }
}

Type guard

function isUnauthorizedError(err: unknown): err is Error {
  return err instanceof Error && err.message === 'Unauthorized request';
}

Try / catch

try {
  const rows = await driver.queryPromised(sql);
} catch (err) {
  if (err instanceof Error && err.message === 'Unauthorized request') {
    // refresh/rotate the Pinot authToken or basicAuth credentials, then retry once
    await refreshCredentials();
    return driver.queryPromised(sql);
  }
  if (err instanceof Error && err.message === 'Unexpected error') {
    // inspect broker health/logs; opaque 4xx/5xx — log context and retry with backoff
    await retryWithBackoff(() => driver.queryPromised(sql));
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/0603828a79e3ac38. Report an issue: GitHub.