Skip to content

Preserve the raw Databricks error code on ApiError.code and stop translating HTTP status into a code#181

Merged
parthban-db merged 2 commits into
mainfrom
parthban-db/stack/bugbash-bug2-apierror-code
Jun 8, 2026
Merged

Preserve the raw Databricks error code on ApiError.code and stop translating HTTP status into a code#181
parthban-db merged 2 commits into
mainfrom
parthban-db/stack/bugbash-bug2-apierror-code

Conversation

@parthban-db

@parthban-db parthban-db commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🥞 Stacked PR

Use this link to review incremental changes.


Summary

Reworks ApiError.code to carry the response's error_code string verbatim — typed as an open enum whose named members are the canonical gRPC codes — and stops translating the HTTP status into a code. A 404 with "error_code": "CATALOG_DOES_NOT_EXIST" now exposes code === 'CATALOG_DOES_NOT_EXIST' instead of collapsing to Code.UNKNOWN, while a response with no string error_code is Code.UNKNOWN rather than a status-derived guess such as Code.NOT_FOUND.

Why

Databricks APIs return product-specific error codes (e.g. CATALOG_DOES_NOT_EXIST, INVALID_PARAMETER_VALUE) in the body's error_code field. fromHttpError resolved code by passing that string through codeFromString, which recognizes only the 17 canonical gRPC names and returns Code.UNKNOWN for anything else — so code was UNKNOWN for essentially every other Databricks error, a guard like if (err.code === Code.NOT_FOUND) was dead even on a genuine 404, and the original string was discarded with no way to recover it. When error_code was missing or an integer, code instead came from toCode, which maps the HTTP status to a canonical code (404 → NOT_FOUND, 400 → INVALID_ARGUMENT, and so on), but the heuristic may not always work.

How is this tested?

apierror.test.ts and codes.test.ts cover the cases above. @databricks/sdk-core format, lint, typecheck, test, and test:browser pass, as do the @databricks/sdk-examples checks.

AI Generated

What changed

Interface changes

  • Code becomes an open string enum — export const Code = { UNKNOWN: 'UNKNOWN', … } as const plus export type Code = (typeof Code)[keyof typeof Code] | (string & {}) — replacing the numeric enum Code. Member spellings are unchanged (Code.NOT_FOUND, etc.); their values are now their own canonical strings rather than integers, and any string is a valid Code.
  • ApiError.code: Code now holds the raw error_code string (canonical or product-specific), or Code.UNKNOWN when the response carries no string error code.
  • Removed toCode (the exported HTTP-status → Code mapping function), along with codeToString and codeFromString: the SDK no longer derives a code from the status, and a Code is already its own string form.

Behavioral changes

  • The HTTP status is no longer translated into a code. The toCode mapping (404 → NOT_FOUND, 400 → INVALID_ARGUMENT, 401 → UNAUTHENTICATED, non-canonical 4xx → FAILED_PRECONDITION, 5xx → INTERNAL, …) is gone. Any response without a usable string error_code — missing, an integer, an empty body, a non-JSON/proxy body, or one that fails schema validation — now yields Code.UNKNOWN instead of a status-derived code. Read httpStatusCode for status-based classification.
  • A string error_code is carried verbatim: "NOT_FOUND" resolves to Code.NOT_FOUND, and "CATALOG_DOES_NOT_EXIST" resolves to 'CATALOG_DOES_NOT_EXIST' (previously Code.UNKNOWN).
  • message, details, httpStatusCode, httpHeader, and httpBody are unchanged.

Internal changes

  • fromHttpError assigns code directly from error_code; the toCode function and the CODE_TO_STRING / STRING_TO_CODE lookup tables are deleted.
  • codes.test.ts is rewritten to cover the open enum (it previously exercised codeToString/codeFromString round-trips); apierror.test.ts cases are updated so a product-specific error_code asserts the verbatim code and responses with no string error_code assert Code.UNKNOWN.
  • The auth-and-errors example logs err.code directly instead of codeToString(err.code).

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Please ensure that the NEXT_CHANGELOG.md file is updated with any relevant changes.
If this is not necessary for your PR, please include the following in your PR description:
NO_CHANGELOG=true
and rerun the job.

## Summary

Fixes `ApiError.code` resolving to `Code.UNKNOWN` for every Databricks-specific error, and exposes the raw `error_code` string as a new `ApiError.errorCode` field so callers can match Databricks-specific codes precisely.

## Why

Databricks APIs return product-specific error codes in the `error_code` field (e.g. a 404 with `"error_code": "CATALOG_DOES_NOT_EXIST"`). `ApiError.fromHttpError` called `codeFromString(error_code)` unconditionally for string codes, but `codeFromString` only knows the canonical gRPC code names; any Databricks-specific string is a table miss and returns `Code.UNKNOWN`. Only the non-string branch (integer or missing `error_code`) fell back to `toCode(httpStatusCode)`. As a result, the canonical `code` was `UNKNOWN` for essentially all real Databricks errors, so a guard like `if (err.code === Code.NOT_FOUND)` was always false even on a genuine 404. Callers also had no way to recover the original `error_code` string, so they could not match on Databricks-specific codes at all.

This is a deliberate divergence from the Go SDK. The Go SDK has the identical canonical-code gap, but it always preserves the raw `error_code` string on a dedicated `APIError.ErrorCode` field. The TypeScript SDK had collapsed the error down to only the canonical `code`, dropping the raw string entirely. This PR closes the gap on both fronts: it adds the status-code fallback so the canonical `code` is meaningful, and it reintroduces the raw string as `errorCode` to match the information the Go SDK preserves.

## What changed

### Interface changes

- **`ApiError.errorCode: string`** — New public readonly field carrying the raw, Databricks-specific error code string from the response (e.g. `"CATALOG_DOES_NOT_EXIST"`). It is `''` when the response did not carry a string error code. Use it to match codes that have no canonical `Code` equivalent.
- **`ApiErrorOptions.errorCode?: string`** — New optional constructor option backing the field.

### Behavioral changes

- When `error_code` is a string that does not map to a canonical `Code` (i.e. `codeFromString` returns `Code.UNKNOWN`), `fromHttpError` now falls back to `toCode(httpStatusCode)` instead of leaving `code` as `UNKNOWN`. A 404 with a Databricks-specific `error_code` now resolves `code` to `Code.NOT_FOUND`. Known canonical string codes (e.g. `"NOT_FOUND"`) continue to map directly, and integer or missing `error_code` values continue to fall back to the status code. The raw string is preserved on the new `errorCode` field regardless of whether the canonical mapping succeeded.

### Internal changes

- Unified the string and non-string `error_code` paths in `fromHttpError`: the raw string (or `''`) is computed once, mapped through `codeFromString`, and falls back to `toCode(statusCode)` on a miss for both paths.

## How is this tested?

Updated `packages/core/tests/apierror/apierror.test.ts`. The case that previously locked in the bug (a Databricks-specific string code asserting `Code.UNKNOWN`) now asserts the corrected status-code fallback (`CATALOG_DOES_NOT_EXIST` on a 404 resolves to `Code.NOT_FOUND` and preserves `errorCode`). The `fromHttpError` runner now also asserts `errorCode` on every case, and the known-canonical-string cases assert their preserved raw string. `npm run build`, `npm test` (357 tests pass), `npm run typecheck`, and `npm run lint` for `@databricks/sdk-core` are all clean.

Co-authored-by: Isaac
@parthban-db parthban-db force-pushed the parthban-db/stack/bugbash-bug2-apierror-code branch from 4cb8f6f to 736f638 Compare June 7, 2026 15:26
@parthban-db parthban-db force-pushed the parthban-db/stack/bugbash-bug2-apierror-code branch from 736f638 to 8a48fef Compare June 8, 2026 11:56
@parthban-db parthban-db marked this pull request as ready for review June 8, 2026 11:58
@parthban-db parthban-db changed the title Fix ApiError.code fallback for Databricks-specific error codes Preserve the raw Databricks error code on an open-enum ApiError.code Jun 8, 2026
@parthban-db parthban-db changed the title Preserve the raw Databricks error code on an open-enum ApiError.code Preserve the raw Databricks error code on ApiError.code and stop translating HTTP status into a code Jun 8, 2026
expect(codeFromString(codeToString(code))).toBe(code);
}
);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test feels like your testing JavaScript. There's no logic behind this, just an export which you are verifying here. Am I reading this wrong?

@parthban-db parthban-db added this pull request to the merge queue Jun 8, 2026
Merged via the queue into main with commit 85903f4 Jun 8, 2026
22 of 27 checks passed
@parthban-db parthban-db deleted the parthban-db/stack/bugbash-bug2-apierror-code branch June 8, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants