fix(cf): classify native CF endpoint errors (unreachable vs auth) in banner - #5385
Merged
norman-abramovitz merged 3 commits intoMay 28, 2026
Merged
Conversation
…ders
Native CF handler failures all collapsed to HTTP 502 with the real
cause buried in the message, so the frontend showed "authentication
expired" for every failure — including an unreachable/down endpoint
or a plain CF error.
Add classifyCfError: type-safe classification of UAA token-refresh
errors (api.ErrHTTPRequest.Status) and capi CF-call errors (capi
sentinels + net/timeout) into two called-out reasons — unreachable
(5xx/network) and auth_expired (401/invalid_grant) — leaving normal
CF errors unclassified.
Surface it on two paths so both reach the frontend:
- classifyNativeErrors middleware wraps the native route group and
shapes raw handler errors (newCapiClient now returns its raw
refresh/client error instead of a pre-wrapped 502).
- handleCapiError (direct-write handlers) sets the same headers.
Both set X-Stratos-Error-Reason and X-Stratos-Upstream-Status (the
real CF/UAA code, since the Stratos status is a mapped 502). Body
uses a '{'-led JSON discriminator. oauth_requests RefreshOAuthToken
now wraps with %w so api.ErrHTTPRequest stays unwrappable.
Replace `echo.NewHTTPError(502, err.Error())` with the raw capi
error at 29 CF-call sites across the native read handlers. The
stringified form erased the error type, so the classifyNativeErrors
middleware could not tell an unreachable endpoint (CF 5xx) from a
normal CF error. Returning the raw error lets the middleware
classify it (and a CF 5xx mid-call now surfaces as unreachable).
String-literal and config errors ("no job id returned", "endpoint
not found", token-parse) are left as echo errors — they are not
capi errors and pass through the middleware unchanged.
The cf-api-interceptor showed "authentication expired. Reconnect" for every native CF 502, even when the endpoint was simply down. Branch on the X-Stratos-Error-Reason header: - auth_expired → reconnect banner + markStale (token rejected) - unreachable → "the endpoint is unreachable or down" + the upstream HTTP code, no markStale/Reconnect (reconnect won't help a down API) - otherwise → neutral "Request failed" banner; normal CF errors (404/422, no reason, non-502) are left to the calling component Two-line banner: endpoint name on line 1, "HTTP <code>: <what>" on line 2 — rendered via the snackbar's whitespace-pre-line (no <br>/innerHTML, so the user-controlled endpoint name can't inject markup). The GUID is dropped from the message; the upstream code comes from X-Stratos-Upstream-Status. EndpointAuthStateService gains notifyOnce() to dedup non-auth banners without marking an endpoint "needs reconnect".
norman-abramovitz
approved these changes
May 28, 2026
norman-abramovitz
left a comment
Contributor
There was a problem hiding this comment.
Saw the 503 error for the endpoint not reachable or down
4 tasks
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.
Summary
Native CF handler failures all collapsed to HTTP 502 with the real cause buried in the message, so Stratos showed "authentication expired. Reconnect to refresh." for every failure — including an endpoint that's simply down (where reconnecting can't help) or a plain CF error.
This classifies the failure and surfaces it accurately.
Backend
classifyCfError— type-safe classification of UAA token-refresh errors (api.ErrHTTPRequest.Status) and capi CF-call errors (capi sentinels + net/timeout) into two called-out reasons:unreachable(5xx/network) andauth_expired(401/invalid_grant). Everything else stays unclassified.classifyNativeErrorsmiddleware wraps the native route group and shapes raw handler errors (newCapiClientnow returns its raw refresh/client error instead of a pre-wrapped 502; 29 read-handler capi-call sites return the raw error).handleCapiError(direct-write handlers) sets the same headers.X-Stratos-Error-Reason+X-Stratos-Upstream-Status(the real CF/UAA code, since the Stratos status is a mapped 502).oauth_requests.RefreshOAuthTokennow wraps with%wso the typed error stays unwrappable.Frontend
cf-api-interceptorbranches on the reason header:auth_expired→ reconnect banner + markStaleunreachable→ "the endpoint is unreachable or down" + the upstream HTTP code, no reconnect/markStaleHTTP <code>: <what>), rendered viawhitespace-pre-line— no<br>/innerHTML, so the user-controlled endpoint name can't inject markup. GUID dropped from the message.EndpointAuthStateService.notifyOnce()dedups non-auth banners without marking an endpoint "needs reconnect".Test plan
classifyCfError,nativeCFError,classifyNativeErrors,upstreamStatusOfunit tests; full cloudfoundry plugin suite green;go build ./...cleancfApiInterceptorspecs (auth_expired / unreachable / generic / header-driven / 404-no-banner) green