cloudwego / cloudwego/eino-ext
Claude Bedrock: Config.HTTPClient causes panic with AWS_CA_BUNDLE + is ignored for API calls
- Dominant language
- Go
- Stars
- 811
- Forks
- 368
- Avg merge
- 16h 22m
- Merged PRs (30d)
- 13
Description
**Describe the bug**
Two bugs related to `Config.HTTPClient` in the Bedrock code path:
1. **Panic when `AWS_CA_BUNDLE` is set**: Passing `*http.Client` to `awsConfig.WithHTTPClient` causes a panic because the AWS SDK type-asserts it to `*awshttp.BuildableClient`
2. **HTTPClient ignored for API calls**: Even without the panic, the custom `HTTPClient` is never used for actual Bedrock API requests — it's only passed to the AWS SDK config (for credential resolution), not to the Anthropic SDK's `option.WithHTTPClient`
Both bugs affect users behind corporate proxies (e.g., Teleport `tsh proxy aws`) who need a custom HTTP client to trust a local CA certificate.
---
## Bug 1: Panic when `AWS_CA_BUNDLE` is set
**To Reproduce**
1. Set `AWS_CA_BUNDLE` to any valid PEM file path
2. Create a Bedrock chat model:
```go
chatModel, err := claude.NewChatModel(ctx, &claude.Config{
ByBedrock: true,
Model: "anthropic.claude-3-5-sonnet-20241022-v2:0",
HTTPClient: &http.Client{},
})
```
3. Panic:
```
interface conversion: *http.Client is not *awshttp.BuildableClient
```
**Root cause**
In `claude.go` (Bedrock path):
```go
if config.HTTPClient != nil {
opts = append(opts, awsConfig.WithHTTPClient(config.HTTPClient))
}
```
This passes `*http.Client` to the AWS SDK config loader. When `AWS_CA_BUNDLE` is set, the AWS SDK's `resolveCustomCABundle` in `resolve.go` does:
```go
cfg.HTTPClient = cfg.HTTPClient.(*awshttp.BuildableClient).WithTransportOptions(...)
```
A plain `*http.Client` doesn't satisfy `*awshttp.BuildableClient`, causing the panic.
---
## Bug 2: HTTPClient not used for actual API calls
**Symptom**: Even with a custom `HTTPClient` that trusts a proxy CA, Bedrock requests fail with TLS certificate errors.
**Root cause**
Compare the Bedrock path vs. the direct API path in `claude.go`:
```go
// Bedrock path — HTTPClient only goes to AWS SDK config (credential resolution)
if config.HTTPClient != nil {
opts = append(opts, awsConfig.WithHTTPClient(config.HTTPClient))
}
cli = anthropic.NewClient(bedrock.WithLoadDefaultConfig(ctx, opts...))
// Direct API path — HTTPClient correctly goes to Anthropic SDK options
if config.HTTPClient != nil {
opts = append(opts, option.WithHTTPClient(config.HTTPClient))
}
cli = anthropic.NewClient(opts...)
```
In the Bedrock path, `config.HTTPClient` is passed to `awsConfig.WithHTTPClient()` (used during AWS credential/config loading), but **not** to `option.WithHTTPClient()` (used for actual Anthropic SDK HTTP requests). The Anthropic SDK falls back to `http.DefaultClient` for all Bedrock API calls, ignoring the user's custom transport/TLS configuration.
---
## Suggested fix
```go
} else if config.ByBedrock {
var opts []func(*awsConfig.LoadOptions) error
// ... region, credentials setup ...
if config.HTTPClient != nil {
// Bug 1 fix: wrap in BuildableClient so AWS_CA_BUNDLE resolution works
opts = append(opts, awsConfig.WithHTTPClient(
awshttp.NewBuildableClient().WithTransportOptions(func(t *http.Transport) {
if tr, ok := config.HTTPClient.Transport.(*http.Transport); ok {
*t = *tr
}
}),
))
}
// Bug 2 fix: pass HTTPClient to Anthropic SDK for actual API calls
bedrockOpts := []option.RequestOption{}
if config.HTTPClient != nil {
bedrockOpts = append(bedrockOpts, option.WithHTTPClient(config.HTTPClient))
}
cli = anthropic.NewClient(
append(bedrockOpts, bedrock.WithLoadDefaultConfig(ctx, opts...))...,
)
```
Alternatively for Bug 1, simply use `awshttp.NewBuildableClient()` as the default HTTP client when none is provided, which is what the AWS SDK does internally.
**Environment**
- eino-ext: latest main
- Go: 1.23+
- AWS SDK Go v2
- anthropic-sdk-go
Contributor guide
Assessment
This issue has not been assessed yet.