馃敶 Required Information
Describe the Bug:
ChatCompletionsHttpClient converts every non-successful HTTP response into a plain IOException whose message contains the OkHttp Response.toString() value and response body:
if (!response.isSuccessful()) {
String bodyStr = body != null ? body.string() : "";
emitter.tryOnError(
new IOException(
"HTTP request failed with status: "
+ response
+ " - body: "
+ bodyStr));
}
As a result, callers cannot inspect the HTTP status programmatically.
For example, implementing a bounded retry for HTTP 429 currently requires parsing text such as:
HTTP request failed with status: Response{protocol=h2, code=429, message=, url=...} - body: ...
This couples callers to:
- the exception message;
- OkHttp's
Response.toString() format;
- field ordering inside that format;
- URL and response body content embedded in the message.
The same issue applies to both streaming and non streaming requests.
Steps to Reproduce:
- Start an HTTP server whose
/v1/chat/completions endpoint returns HTTP 429.
- Construct a
ChatCompletionsHttpClient pointing to that server.
- Call
complete(...).
- Inspect the emitted failure.
Example:
HttpOptions options = HttpOptions.builder()
.baseUrl("http://localhost:8080/v1")
.build();
ChatCompletionsClient client =
new ChatCompletionsHttpClient(options);
client.complete(request, false)
.test()
.assertError(IOException.class);
The resulting exception has no status accessor. The only representation of 429 is inside getMessage().
Expected Behavior:
Non successful HTTP responses should be emitted through a typed exception that exposes the HTTP status separately from its presentation message.
For example:
public final class ChatCompletionsHttpException extends IOException {
private final int statusCode;
private final String responseBody;
public int statusCode() {
return statusCode;
}
public String responseBody() {
return responseBody;
}
}
The client could then emit an exception constructed from the HTTP response, for example:
new ChatCompletionsHttpException(
response.code(),
response.message(),
bodyStr);
Alternatively, ADK could use an existing exception type that exposes a stable numeric HTTP status code.
The important requirement is that callers can inspect the HTTP status without parsing Throwable.getMessage().
Observed Behavior:
All non success statuses are emitted as plain IOException instances:
A caller cannot safely distinguish between:
- HTTP 401;
- HTTP 429;
- HTTP 500;
- a connection level
IOException;
- an exception whose response body happens to contain
"code":429.
The only way to determine the HTTP status is currently to parse the exception message.
Environment Details:
- ADK Library Version (see Maven dependency):
com.google.adk:google-adk:1.7.1
- OS: N/A
- TS Version (
tsc --version): N/A (Java ADK)
Model Information:
- Which model is being used: N/A (the issue concerns HTTP error handling in
ChatCompletionsHttpClient, independent of the model)
馃煛 Optional Information
Regression:
N/A. The current ChatCompletionsHttpClient source appears to retain the same behavior.
Logs:
N/A. A representative failure message is:
HTTP request failed with status: Response{protocol=h2, code=429, message=, url=...} - body: ...
Screenshots / Video:
N/A.
Additional Context:
OpenAI compatible gateways commonly use HTTP 429 for temporary rate limiting. Applications may need to apply bounded retry/backoff specifically to that status while immediately surfacing authentication and request errors.
With a typed exception, retry code becomes stable and explicit:
if (failure instanceof ChatCompletionsHttpException exception
&& exception.statusCode() == 429) {
// Apply bounded backoff.
}
Without a typed exception, callers must use brittle message parsing:
Pattern.compile(
"^HTTP request failed with status: Response\\{...code=429...");
Message parsing can break after an OkHttp or ADK formatting change and can accidentally classify response body text as transport metadata.
Security consideration
The current exception message includes the full response representation and response body. Depending on endpoint configuration, these may contain URL details or provider specific information that applications unintentionally write to logs.
A typed exception would allow applications to log only safe metadata, such as:
while retaining response details as explicitly accessed fields when needed.
Scope
This request does not require ADK to implement a retry policy.
It only asks ChatCompletionsHttpClient to preserve machine readable HTTP response metadata so callers, or a future OpenAiCompatibleLlm, can implement their own retry policy safely.
Minimal Reproduction Code:
HttpOptions options = HttpOptions.builder()
.baseUrl("http://localhost:8080/v1")
.build();
ChatCompletionsClient client =
new ChatCompletionsHttpClient(options);
client.complete(request, false)
.test()
.assertError(IOException.class);
The test server should return HTTP 429 from /v1/chat/completions.
Suggested Tests:
Add streaming and non streaming tests asserting that:
- HTTP 429 emits the typed exception with
statusCode() == 429.
- HTTP 401 emits the same type with
statusCode() == 401.
- Connection failures remain ordinary
IOException.
- Response body text containing
"code":429 does not affect the exposed transport status.
- Existing error body information remains available without being required in the exception message.
How often has this issue occurred?:
馃敶 Required Information
Describe the Bug:
ChatCompletionsHttpClientconverts every non-successful HTTP response into a plainIOExceptionwhose message contains the OkHttpResponse.toString()value and response body:As a result, callers cannot inspect the HTTP status programmatically.
For example, implementing a bounded retry for HTTP 429 currently requires parsing text such as:
This couples callers to:
Response.toString()format;The same issue applies to both streaming and non streaming requests.
Steps to Reproduce:
/v1/chat/completionsendpoint returns HTTP 429.ChatCompletionsHttpClientpointing to that server.complete(...).Example:
The resulting exception has no status accessor. The only representation of
429is insidegetMessage().Expected Behavior:
Non successful HTTP responses should be emitted through a typed exception that exposes the HTTP status separately from its presentation message.
For example:
The client could then emit an exception constructed from the HTTP response, for example:
Alternatively, ADK could use an existing exception type that exposes a stable numeric HTTP status code.
The important requirement is that callers can inspect the HTTP status without parsing
Throwable.getMessage().Observed Behavior:
All non success statuses are emitted as plain
IOExceptioninstances:A caller cannot safely distinguish between:
IOException;"code":429.The only way to determine the HTTP status is currently to parse the exception message.
Environment Details:
com.google.adk:google-adk:1.7.1tsc --version): N/A (Java ADK)Model Information:
ChatCompletionsHttpClient, independent of the model)馃煛 Optional Information
Regression:
N/A. The current
ChatCompletionsHttpClientsource appears to retain the same behavior.Logs:
N/A. A representative failure message is:
Screenshots / Video:
N/A.
Additional Context:
OpenAI compatible gateways commonly use HTTP 429 for temporary rate limiting. Applications may need to apply bounded retry/backoff specifically to that status while immediately surfacing authentication and request errors.
With a typed exception, retry code becomes stable and explicit:
Without a typed exception, callers must use brittle message parsing:
Message parsing can break after an OkHttp or ADK formatting change and can accidentally classify response body text as transport metadata.
Security consideration
The current exception message includes the full response representation and response body. Depending on endpoint configuration, these may contain URL details or provider specific information that applications unintentionally write to logs.
A typed exception would allow applications to log only safe metadata, such as:
while retaining response details as explicitly accessed fields when needed.
Scope
This request does not require ADK to implement a retry policy.
It only asks
ChatCompletionsHttpClientto preserve machine readable HTTP response metadata so callers, or a futureOpenAiCompatibleLlm, can implement their own retry policy safely.Minimal Reproduction Code:
The test server should return HTTP 429 from
/v1/chat/completions.Suggested Tests:
Add streaming and non streaming tests asserting that:
statusCode() == 429.statusCode() == 401.IOException."code":429does not affect the exposed transport status.How often has this issue occurred?: