Preserve the raw Databricks error code on ApiError.code and stop translating HTTP status into a code#181
Merged
Conversation
|
Please ensure that the NEXT_CHANGELOG.md file is updated with any relevant changes. |
8671804 to
357b416
Compare
## 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
4cb8f6f to
736f638
Compare
736f638 to
8a48fef
Compare
ApiError.code
ApiError.codeApiError.code and stop translating HTTP status into a code
mihaimitrea-db
approved these changes
Jun 8, 2026
| expect(codeFromString(codeToString(code))).toBe(code); | ||
| } | ||
| ); | ||
| }); |
Contributor
There was a problem hiding this comment.
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?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🥞 Stacked PR
Use this link to review incremental changes.
Summary
Reworks
ApiError.codeto carry the response'serror_codestring 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 exposescode === 'CATALOG_DOES_NOT_EXIST'instead of collapsing toCode.UNKNOWN, while a response with no stringerror_codeisCode.UNKNOWNrather than a status-derived guess such asCode.NOT_FOUND.Why
Databricks APIs return product-specific error codes (e.g.
CATALOG_DOES_NOT_EXIST,INVALID_PARAMETER_VALUE) in the body'serror_codefield.fromHttpErrorresolvedcodeby passing that string throughcodeFromString, which recognizes only the 17 canonical gRPC names and returnsCode.UNKNOWNfor anything else — socodewasUNKNOWNfor essentially every other Databricks error, a guard likeif (err.code === Code.NOT_FOUND)was dead even on a genuine 404, and the original string was discarded with no way to recover it. Whenerror_codewas missing or an integer,codeinstead came fromtoCode, 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.tsandcodes.test.tscover the cases above.@databricks/sdk-coreformat, lint, typecheck, test, and test:browser pass, as do the@databricks/sdk-exampleschecks.AI Generated
What changed
Interface changes
Codebecomes an open string enum —export const Code = { UNKNOWN: 'UNKNOWN', … } as constplusexport type Code = (typeof Code)[keyof typeof Code] | (string & {})— replacing the numericenum 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 validCode.ApiError.code: Codenow holds the rawerror_codestring (canonical or product-specific), orCode.UNKNOWNwhen the response carries no string error code.toCode(the exported HTTP-status →Codemapping function), along withcodeToStringandcodeFromString: the SDK no longer derives a code from the status, and aCodeis already its own string form.Behavioral changes
toCodemapping (404 →NOT_FOUND, 400 →INVALID_ARGUMENT, 401 →UNAUTHENTICATED, non-canonical 4xx →FAILED_PRECONDITION, 5xx →INTERNAL, …) is gone. Any response without a usable stringerror_code— missing, an integer, an empty body, a non-JSON/proxy body, or one that fails schema validation — now yieldsCode.UNKNOWNinstead of a status-derived code. ReadhttpStatusCodefor status-based classification.error_codeis carried verbatim:"NOT_FOUND"resolves toCode.NOT_FOUND, and"CATALOG_DOES_NOT_EXIST"resolves to'CATALOG_DOES_NOT_EXIST'(previouslyCode.UNKNOWN).message,details,httpStatusCode,httpHeader, andhttpBodyare unchanged.Internal changes
fromHttpErrorassignscodedirectly fromerror_code; thetoCodefunction and theCODE_TO_STRING/STRING_TO_CODElookup tables are deleted.codes.test.tsis rewritten to cover the open enum (it previously exercisedcodeToString/codeFromStringround-trips);apierror.test.tscases are updated so a product-specificerror_codeasserts the verbatimcodeand responses with no stringerror_codeassertCode.UNKNOWN.auth-and-errorsexample logserr.codedirectly instead ofcodeToString(err.code).