Skip to content

Commit 1122277

Browse files
authored
testproxy: forward raw error body and headers from upstream (#5263)
The reverse proxy in `libs/testproxy` re-marshalled `apierr.APIError` into a `{error_code, message}` envelope, dropping `details[]` and any other fields the workspace returned. As a result, acceptance tests run against the cloud could not observe error metadata that real CLI/TF invocations rely on. This change forwards `apiErr.ResponseWrapper.DebugBytes` verbatim with the original status code, so callers see exactly what the workspace sent. As a knock-on fix, response headers in `includeResponseHeaders` (e.g. `X-Databricks-Org-Id`) are now also passed through on the error path — previously the `WithResponseHeader` visitors weren't invoked when `apiClient.Do` returned an error. `ResponseWrapper` has been populated on every `APIError` since [databricks-sdk-go#1261](databricks/databricks-sdk-go#1261) (v0.100.0); the CLI is on v0.132.0. A panic guards the invariant in case the SDK ever changes shape.
1 parent a45b2af commit 1122277

1 file changed

Lines changed: 21 additions & 13 deletions

File tree

libs/testproxy/server.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package testproxy
22

33
import (
44
"bytes"
5-
"encoding/json"
65
"errors"
76
"net/http"
87
"net/http/httptest"
@@ -129,22 +128,31 @@ func (s *ProxyServer) proxyToCloud(w http.ResponseWriter, r *http.Request) {
129128

130129
var encodedResponse *testserver.EncodedResponse
131130

132-
// API errors from the SDK are expected to be of the type [apierr.APIError]. If we
133-
// get an API error then parse the error and forward it back to the client
134-
// in an appropriate format.
131+
// API errors from the SDK are of type [apierr.APIError]. Forward the raw
132+
// response bytes verbatim — including any error details — so callers see
133+
// exactly what the workspace returned. Re-marshalling from the parsed
134+
// APIError would drop fields the SDK doesn't surface (e.g. metadata in
135+
// details[]) and silently break callers that inspect them.
135136
apiErr := &apierr.APIError{}
136137
if errors.As(err, &apiErr) {
137-
body := map[string]string{
138-
"error_code": apiErr.ErrorCode,
139-
"message": apiErr.Message,
138+
rw := apiErr.ResponseWrapper
139+
if rw == nil {
140+
// The SDK populates ResponseWrapper for every APIError produced
141+
// from a real HTTP response. If this ever fires the SDK changed
142+
// shape and we need to revisit how we forward error bodies.
143+
panic("apierr.APIError has no ResponseWrapper")
140144
}
141-
142-
b, err := json.Marshal(body)
143-
assert.NoError(s.t, err)
144-
145145
encodedResponse = &testserver.EncodedResponse{
146-
StatusCode: apiErr.StatusCode,
147-
Body: b,
146+
StatusCode: rw.Response.StatusCode,
147+
Body: rw.DebugBytes,
148+
}
149+
// Visitors registered via WithResponseHeader are not invoked when
150+
// the SDK returns an error, so populate the include list directly
151+
// from the original response headers.
152+
for _, header := range includeResponseHeaders {
153+
if v := rw.Response.Header.Get(header); v != "" {
154+
*responseHeaders[header] = v
155+
}
148156
}
149157
}
150158

0 commit comments

Comments
 (0)