Skip to content

fix(aws,bedrock): raise CredentialsError when the session resolves no credentials - #2059

Open
Steve-20-ai wants to merge 1 commit into
anthropics:mainfrom
Steve-20-ai:fix/bedrock-missing-credentials-error
Open

Steve-20-ai wants to merge 1 commit into
anthropics:mainfrom
Steve-20-ai:fix/bedrock-missing-credentials-error

Conversation

@Steve-20-ai

@Steve-20-ai Steve-20-ai commented Nov 9, 2026 •

Copy link
Copy Markdown

Problem

AnthropicBedrock / AsyncAnthropicBedrock with no resolvable AWS credentials raise a builtin RuntimeError from the signing path:

$ python repro.py
type: builtins.RuntimeError
message: Could not resolve AWS credentials from session
isinstance AnthropicError: False

anthropic>=2 already ships anthropic.CredentialsError(AnthropicError) — documented as "credentials cannot be loaded from local files, config, or the environment", which is exactly this case — but the signing helper raises a bare RuntimeError instead. Callers and wrappers therefore cannot classify a missing-credentials configuration problem with except anthropic.AnthropicError, and it is not turned into APIConnectionError either, because signing runs in _prepare_request() before the try around self._client.send(...).

Fix

from ..._exceptions import CredentialsError
...
    credentials = session.get_credentials()
    if not credentials:
        raise CredentialsError("Could not resolve AWS credentials from session")

CredentialsError is what the SDK defines for this situation, it is an AnthropicError, and it is deliberately not a RetryableError — retrying cannot conjure credentials, so the error surfaces immediately instead of burning max_retries.

Compatibility

RuntimeError is not currently documented for this path, and CredentialsError subclasses AnthropicError, not RuntimeError, so code that catches the old bare RuntimeError specifically will stop matching. That is the point of the change, but it is worth a release note. Everything that catches AnthropicError — the documented way to catch SDK errors — now catches this case for the first time.

Rebuilt on the current main

The first revision patched src/anthropic/lib/bedrock/_auth.py. That module no longer exists: the SigV4 signing helper was lifted to src/anthropic/lib/aws/_auth.py and is now shared by the Bedrock client and the AWS/mantle client (lib/bedrock/_client.py imports from ..aws._auth import get_auth_headers, lib/aws/_client.py does the same). The guard is the same line, in the new home:

src/anthropic/lib/aws/_auth.py:65
    if not credentials:
        raise RuntimeError("Could not resolve AWS credentials from session")

So the branch is rebuilt on main at d4976414 and the one-line change now sits in lib/aws/_auth.py. A side effect worth noting: lib/aws/_client.py reaches the same helper, so an AnthropicAWSClient with no resolvable credentials now also gets CredentialsError rather than RuntimeError. That is the same class of problem, and the alternative — leaving that client on RuntimeError — would mean two error types for one condition.

The tests patch anthropic.lib.aws._auth._get_session rather than the old bedrock module, and there is now a second test for the async client, since the async path goes through asyncify(get_auth_headers) and could plausibly wrap the error differently.

Validation

Both tests use a session whose get_credentials() returns None, so they do not depend on the ambient AWS configuration of the machine running them.

main (d4976414) + new tests only:  6 failed, 82 passed
                                    FAILED test_missing_credentials_raise_credentials_error
                                    FAILED test_missing_credentials_raise_credentials_error_async
                                      E   RuntimeError: Could not resolve AWS credentials from session
this branch:                       4 failed, 84 passed

The four remaining failures are identical on both sides: test_region_infer_from_profile and the test_region_infer_from_specified_profile / test_region_infer_from_aws_profile_argument parameters. They read ~/.aws/config and fail with ProfileNotFound / ValueError in this Windows sandbox where no such profile exists; they are unrelated to the change and untouched by it.

ruff check and ruff format --check on both files: clean.

Adjacent issues not covered here

  • lib/bedrock/_client.py also raises a bare RuntimeError("Expected dictionary json_data for post /v1/messages endpoint"). That one is an internal invariant violation rather than a configuration problem, so CredentialsError would be the wrong class; left alone.
  • The region-inference paths that raise ValueError("No AWS region was provided") are untouched.
  • AnthropicAWSClient is covered by the same one-line change but has no test of its own here; it would need the same _get_session stub against lib/aws/_client.py.

Agent involvement

Written with an agent (WorkBuddy coding agent, DeepSeek-V4.1-Flash). The reproduction, the red/green numbers above and the diff were produced and verified locally, the rebuild against the current main included.

@Steve-20-ai
Steve-20-ai requested a review from a team as a code owner November 9, 2026 11:58
…dentials

The signing helper moved to lib/aws/_auth.py and is now shared by the
Bedrock and AWS (mantle) clients, so the guard lives there and both
clients get the error class.
@Steve-20-ai
Steve-20-ai force-pushed the fix/bedrock-missing-credentials-error branch from c4bfe5f to 053e5c2 Compare November 10, 2026 11:17
@Steve-20-ai Steve-20-ai changed the title fix(bedrock): raise CredentialsError when the session resolves no credentials fix(aws,bedrock): raise CredentialsError when the session resolves no credentials Nov 10, 2026
@Steve-20-ai

Copy link
Copy Markdown
Author

Rebuilt on main (d4976414). The patch was conflicting because src/anthropic/lib/bedrock/_auth.py has since been removed — the SigV4 helper moved to src/anthropic/lib/aws/_auth.py and is now shared with the AWS/mantle client. The same guard, now applied in the new home:

credentials = session.get_credentials()
if not credentials:
    raise CredentialsError("Could not resolve AWS credentials from session")

The diff is back to +2/-1 in lib/aws/_auth.py plus the tests, and the branch is ahead 1, behind 0.

Two things changed beyond the port:

  • The title no longer says bedrock, because lib/aws/_client.py reaches the same helper — an AnthropicAWSClient with no resolvable credentials now gets CredentialsError too, and calling it a bedrock fix would hide that.
  • There is a second test for the async client. That path builds headers through asyncify(get_auth_headers), so it could have wrapped the error independently of the sync path; it does not, but the test pins it.

Red/green on the rebuilt tree, pytest tests/lib/test_bedrock.py:

main + tests only:  6 failed, 82 passed
this branch:        4 failed, 84 passed

The four failures in both runs are the test_region_infer_from_* cases, which need a ~/.aws/config this Windows sandbox does not have and which I did not touch.

@mangeshraut712 mangeshraut712 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mapping a missing session to CredentialsError is the right class: it is an AnthropicError and not an APIConnectionError, so except AnthropicError works and the client will not retry a config problem. The tests only cover get_credentials() returning None; the live boto3/botocore path can fail in other ways.

credentials = session.get_credentials()
if not credentials:
raise RuntimeError("Could not resolve AWS credentials from session")
raise CredentialsError("Could not resolve AWS credentials from session")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session.get_credentials() is not guaranteed to return None. botocore often raises botocore.exceptions.NoCredentialsError (or TokenRetrievalError / UnauthorizedSSOTokenError for SSO and refresh failures) instead of a silent miss. Those would still escape as raw botocore exceptions, so the except CredentialsError / except AnthropicError contract this PR is aiming for would not hold on a real empty or broken credential chain — only on the mocked return None path.

I'd catch those botocore errors here and re-raise CredentialsError (with the original as __cause__), and add a test that stubs get_credentials to raise NoCredentialsError. Worth a quick grep for any remaining RuntimeError("Could not resolve AWS credentials") in tests/lib/test_aws_auth.py or other signing helpers so the swap is complete.

@agammann agammann left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_auth_headers is shared by Bedrock and AnthropicAWSClient, so this also changes the latter’s public exception type. The new tests exercise only AnthropicBedrock and AsyncAnthropicBedrock; could a client call through AnthropicAWSClient with get_credentials() returning None assert it raises CredentialsError? That would cover the second affected client path.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants