Repository navigation
fix: show why a write or load failed - #6016
Merged
norman-abramovitz merged 16 commits intoOct 15, 2026
Merged
norman-abramovitz merged 16 commits into
norman-abramovitz merged 16 commits into
Conversation
The same broker failure read differently depending on how CF answered.
Inside the fast-path window the message was the broker's detail; after
a polled job it was CF's title in front of it ("CF-UnableToPerform.
..."), and only the polled path removed the HTML page CF quotes from a
broker's response.
A failed job's message is now the detail, markup stripped, or CF's
title when there is no detail; extractHttpErrorMessage reads the
fast-path body the same way, through the same stripMarkup. Callers add
their own lead-in. The three expected failures from cloudfoundry#6005 now pass.
Refs cloudfoundry#6002
The failure message now shows only the broker's or CF's explanation, so the CF error code and title it leaves out are logged to the browser console instead. writeWithJob does it, as the one place both the fast-path HTTP error and the polled FAILED job pass through, however each caller formats the message. Refs cloudfoundry#6002
The create-service-instance step and CsiModeService each carried a private copy of extractHttpErrorMessage. Use the shared one, so the markup stripping and the fallbacks live in one place.
HttpErrorResponse is not an Error, so a failure that came back on the fast path lost its reason wherever the message was built from err.message or String(err): deleting a service instance from a list and unbinding on its summary page showed "[object Object]", binding a route service and creating or deleting a service key "undefined (502)", detaching bindings "unknown error", and the orphan-route message Angular's "Http failure response for ...". A rollback showed CF's title from a failed job and "Rollback failed" otherwise. All of them now use extractHttpErrorMessage. The service key credentials read keeps httpErrorResponseToSafeString: it is not a write and names the HTTP status on purpose. Refs cloudfoundry#6002
Creating or editing an organization, quota or space quota built its failure from err.message or String(err). The HTTP error is not an Error, so CF's reason (a duplicate name, say) showed as "[object Object]". Use extractHttpErrorMessage.
extractHttpErrorMessage and stripMarkup lived in cloud-foundry, so core and kubernetes, which sit below it, could not use them and built their messages from err.message or String(err) instead. Move both to @stratosui/store beside httpErrorResponseToSafeString and import them from there everywhere. A thrown string now keeps its text instead of becoming "unknown error". No other behaviour change.
The stepper, the detail page's error banner and header actions, and the add API key dialog built their message from err.message or String(err). An HttpErrorResponse is not an Error, so a failure there read "[object Object]". The stepper is the one most users meet: any wizard step whose submit rejects with the HTTP error showed it. Use extractHttpErrorMessage.
The org, space, CF info and endpoint data services, app detail and the relation fetcher recorded a failed fetch as err.message or String(err), so an HTTP error showed as "[object Object]" in their error states, and the create-service-instance setup logged it the same way. Use extractHttpErrorMessage. The relation post-processor and the kube envelope catch only synchronous throws and keep their own handling.
A write whose CF job reached FAILED inside the fast-path window was
answered 502, so the frontend read a broker rejecting a provision as
the endpoint failing and showed "Cloud Foundry endpoint ... Request
failed" next to the real error. The endpoint had answered; the
operation it ran failed. RunFastPath's own contract already said 4xx.
The 22 handlers that wrote the envelope now call
stratosjobs.WriteFailed: 422, the same {state, errors} body, and a new
X-Stratos-Error-Reason value, operation_failed. The header constant
moves to stratosjobs so the CF plugin and any later upstream (BOSH,
CredHub, UAA) classify failures the same way. A transport failure
never marks a job FAILED (the translator keeps it PROCESSING), so
endpoint faults keep their own reasons.
Refs cloudfoundry#6002
The CF API interceptor raised its endpoint banner for any reason header. Limit it to the reasons that name an endpoint fault (auth_expired, unreachable) and to an unclassified 502; other reasons, such as operation_failed, are left to the component that made the call. Refs cloudfoundry#6002
The Form/JSON radios bound the view choice with ngModel. Inside the create-service-instance step's form group, each tried to register as a control and Angular warned NG01354 twice. The choice is view state, so the radios now bind checked and set the signal on change, without FormsModule. Each schema form gets its own radio group name and ids: the wizard can show two of them (instance and binding parameters).
On the red error snackbar the primary-blue action measured 1.02:1 against the background and the grey close 1.48:1; even the white message was 3.76:1 in light mode and 2.77:1 in dark, under the WCAG AA 4.5:1 for text. Error snackbars now use danger-700, where white is 4.98:1, with a white underlined action and a white close. Other snackbars are unchanged.
CodeQL flagged stripMarkup's tag pattern as a polynomial regular expression on uncontrolled data. With only '>' excluded, a run of '<' and no '>' (the text is the server's error body) backtracks quadratically: 100k characters took 4.3 s. Excluding '<' from the tag body as well keeps it linear, and a stray '<' in prose no longer swallows the text up to the next tag.
norman-abramovitz
approved these changes
Oct 15, 2026
norman-abramovitz
left a comment
Contributor
There was a problem hiding this comment.
LGTM - CodeQL fix
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.
A failed write told the user different things depending on how Cloud Foundry answered. When the job failed inside the fast-path window, the banner showed the error title; after polling, it showed the detail; and many other write and load paths showed
[object Object],undefined (502)orHttp failure response for …instead of the reason the server gave. This is the first group of #6002 (step 1 of its plan) plus the read paths found on the way.One message for a failed write. A failed write now reads as Stratos's lead-in followed by the broker's or Cloud Foundry's own words (the detail, else the title), whichever path the job took. Any HTML page the error quoted is reduced to its text, as before. The CF error code and title are logged to the browser console (
console.warn), so they are there for whoever needs them without crowding the banner.A single helper for HTTP error text.
extractHttpErrorMessageandstripMarkupnow live in@stratosui/store, and every caller imports them from there; the local copies are gone. The paths that showed no reason now use it:[object Object][object Object]undefined (502)Http failure response for …unknown errorRollback failed[object Object][object Object][object Object]A rejected operation is not an endpoint fault. A job that failed inside the fast-path window was answered as 502, so the interceptor also raised "Cloud Foundry endpoint '…' Request failed." The backend now answers it through one helper,
stratosjobs.WriteFailed: 422 with the same{state, errors}body andX-Stratos-Error-Reason: operation_failed(the header constant moved intostratosjobsso later upstreams classify the same way).RunFastPath's doc already promised a 4xx here; 22 handlers had drifted to 502. A transport failure never marks a job FAILED, so real endpoint faults keepunreachableandauth_expired. The frontend raises its endpoint banner only for those two reasons or an unclassified 502. Three contract goldens were regenerated (502 → 422).Found during the live runs:
ngModelinside the form, logging NG01354. It is now plain radios with a per-instance name and ids (the wizard can show two schema forms).danger-700(white 4.98:1) with a white underlined action and close button.Tried:
make check gatepasses (lint, 3827 unit tests, Go tests, production build warnings). Live against a CF with overview-broker, both the fast path (sync+servererror) and the polled path (async+failasync) show one banner with the broker's message and log the CF code; the fast-path POST answers 422 and no endpoint banner appears; NG01354 is gone; the snackbar is readable.Refs #6002.
Changelog fragment:
changelog.d/0008-write-error-messages.md.AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.