You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
No media type is set on the aborted response, so Quarkus REST negotiates it from the resource method -- i.e. from
its @Produces, or from the default for its return type. A resource returning a POJO is declared application/json, so the client receives:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
Service Unavailable (Goblin chaos)
The body is not valid JSON. Any client (or agent) that parses the error payload fails on it, and a resilience test
asserting on the error shape cannot be trusted. It also means an agent that reports "the endpoint returned 503" is
reporting a response that a real service would never send.
Same defect, same fix, second call site: DependencyDegradationAssault aborts with Response.status(503).entity("Dependency unavailable (Goblin chaos)").build() -- same missing type. The INTERMITTENT profile (HTTP 500) and SLOW_FAILURE reach it too. Fixing only httpStatus would leave the identical
bug one assault over.
First step: pin the actual behaviour
The roadmap flagged this as "to be confirmed", and the code explains why the reported Content-Type appears, but
the negotiated type depends on the resource's @Produces and on the Quarkus REST default. Before choosing a fix, add a
failing test that pins what is actually emitted for (a) a resource returning a POJO, (b) a resource returning a String, (c) a resource with an explicit @Produces, and keep its output as the reference for the fix.
Design decisions to make
Honest type, or matching body? Two coherent options:
declare what we send -- set the media type explicitly to text/plain on the abort, since the entity is always
a plain String. Simplest, and never lies. It is a user-visible change: clients that switch on the content type
of the 503 would see text/plain instead of application/json.
send what we declare -- when the resource produces JSON, emit a JSON body (e.g. {"message": "..."}), and text/plain otherwise. The client contract is preserved, and the body becomes machine-readable -- which is what
makes assertions on the client-visible outcome (feat: post-assault assertions (resilience verification) #50) practical. It requires reading the resource's @Produces,
which GoblinChaosFilter already does by reflection for targeting.
Recommend (2), with (1) as the fallback when the resource declares no JSON type. Rationale: a chaos tool exists to
make failures realistic, and a 503 with an unparseable body is not what the real service sends. Option (2) also
gives the post-assault assertions a real payload to assert on.
Which body shape? If (2), the JSON shape must be stable and documented -- {"message": "..."} is the obvious
default, but an error code field would help agents classify the failure. Keep it minimal and documented.
Exception vs status assaults: the exception assault is a different path (an exception thrown before the endpoint
runs, handled by Quarkus REST's mapper). It is out of scope here, but the fix should not create an inconsistency with
it -- check what content type an assaulted-thrown exception produces and note it in the docs if the two differ.
Acceptance criteria
A test-first commit pins the current, wrong Content-Type for each affected call site, and the fix turns it green.
The HTTP status assault answers with a body that is valid for its declared content type, on every resource shape
(POJO, String, explicit @Produces, no entity).
The dependency degradation assault is fixed at the same time -- the two are covered by the same test, and the fix is
shared rather than duplicated per assault.
No regression on the happy paths: the response body and header assaults, the Markdown report and the Dev UI history
are untouched by the change.
The chosen body/JSON shape is documented in assault-types.adoc, and the behaviour change gets a release-notes entry
(clients may observe a different content type than before).
If (2) is chosen, an integration test proves an assaulted 503 on a JSON resource is parseable, and that a text/plain resource still gets text/plain.
v0.4.0 -- Resilience verification, human or agent-driven
Found by an AI agent during a live test on the
quarkus-goblin-demoapplication.Problem
The
httpStatusassault onHTTP_INanswers a plain-text body with aContent-Type: application/jsonheader.HttpStatusAssaultaborts the request like this:No media type is set on the aborted response, so Quarkus REST negotiates it from the resource method -- i.e. from
its
@Produces, or from the default for its return type. A resource returning a POJO is declaredapplication/json, so the client receives:The body is not valid JSON. Any client (or agent) that parses the error payload fails on it, and a resilience test
asserting on the error shape cannot be trusted. It also means an agent that reports "the endpoint returned 503" is
reporting a response that a real service would never send.
Same defect, same fix, second call site:
DependencyDegradationAssaultaborts withResponse.status(503).entity("Dependency unavailable (Goblin chaos)").build()-- same missing type. TheINTERMITTENTprofile (HTTP 500) andSLOW_FAILUREreach it too. Fixing onlyhttpStatuswould leave the identicalbug one assault over.
First step: pin the actual behaviour
The roadmap flagged this as "to be confirmed", and the code explains why the reported
Content-Typeappears, butthe negotiated type depends on the resource's
@Producesand on the Quarkus REST default. Before choosing a fix, add afailing test that pins what is actually emitted for (a) a resource returning a POJO, (b) a resource returning a
String, (c) a resource with an explicit@Produces, and keep its output as the reference for the fix.Design decisions to make
text/plainon the abort, since the entity is alwaysa plain
String. Simplest, and never lies. It is a user-visible change: clients that switch on the content typeof the 503 would see
text/plaininstead ofapplication/json.{"message": "..."}), andtext/plainotherwise. The client contract is preserved, and the body becomes machine-readable -- which is whatmakes assertions on the client-visible outcome (feat: post-assault assertions (resilience verification) #50) practical. It requires reading the resource's
@Produces,which
GoblinChaosFilteralready does by reflection for targeting.make failures realistic, and a 503 with an unparseable body is not what the real service sends. Option (2) also
gives the post-assault assertions a real payload to assert on.
{"message": "..."}is the obviousdefault, but an error code field would help agents classify the failure. Keep it minimal and documented.
runs, handled by Quarkus REST's mapper). It is out of scope here, but the fix should not create an inconsistency with
it -- check what content type an assaulted-thrown exception produces and note it in the docs if the two differ.
Acceptance criteria
Content-Typefor each affected call site, and the fix turns it green.(POJO,
String, explicit@Produces, no entity).shared rather than duplicated per assault.
are untouched by the change.
assault-types.adoc, and the behaviour change gets a release-notes entry(clients may observe a different content type than before).
text/plainresource still getstext/plain.