Symptom
A Bedrock call fails straight away, often after someone changes to a newer model:
ValidationException: Invocation of model ID <model-id> with on-demand throughput
isn't supported. Retry your request with the ID or ARN of an inference profile
that contains this model.
Retries won't help. This model has to be called through an inference profile, so the fix is to change the modelId and then make sure the app's permissions cover the Regions behind that profile.
Quick checks
Run these as the app's role, in the Region the app calls.
REGION=us-east-1
# Which role is actually in use
aws sts get-caller-identity
# How the model is offered in this Region
aws bedrock list-foundation-models --region "$REGION" --by-provider anthropic \
--query "modelSummaries[].[modelId,inferenceTypesSupported]" --output json
# Which system-defined profiles you can call from this Region
aws bedrock list-inference-profiles --region "$REGION" --type-equals SYSTEM_DEFINED \
--query "inferenceProfileSummaries[?status=='ACTIVE'].[inferenceProfileId,inferenceProfileName]" \
--output table
If ON_DEMAND isn't in a model's inferenceTypesSupported list, don't expect a direct call with the plain model ID to work. Change --by-provider to match your model's provider.
Symptom → cause → fix
| Symptom | Likely cause | Fix |
|---|---|---|
on-demand throughput isn't supported | Plain model ID used for a profile-only model | Use the profile ID or ARN as modelId |
The provided model identifier is invalid | Profile prefix isn't available from this source Region, such as eu. called from us-east-1 | Pick a profile from list-inference-profiles in the calling Region |
AccessDeniedException on a foundation-model ARN | Policy allows the profile but not the model in every destination Region | Add the model ARN for each destination Region |
AccessDeniedException on an inference-profile ARN | Policy covers only foundation-model resources | Add the profile ARN |
explicit deny in a service control policy | SCP blocks a destination Region | Allow it, or add a cross-Region exemption |
ThrottlingException after the switch | Cross-Region quotas are separate from the on-demand quotas you had raised | Check and raise the cross-Region quotas |
| Model works in the console but not from the app | Different role or Region, or the app still sends the old model ID | Test with the CLI as the app's role and log the modelId in use |
Step 1: Choose the right profile
Profile IDs add a prefix to the model ID. Common prefixes are:
| Prefix | Routes to | Use when |
|---|---|---|
us., eu., apac. and others | A fixed set of Regions in that geography | Data has to stay in a geography |
global. | Any supported commercial Region | No residency constraint, and the model offers it |
List the destination Regions before you commit. Do this from every source Region you use, because AWS warns against working out the routing from the prefix alone:
aws bedrock get-inference-profile --region "$REGION" \
--inference-profile-identifier us.anthropic.claude-sonnet-4-5-20250929-v1:0 \
--query 'models[].modelArn'
The Region in each returned ARN is a destination Region. The model above is an example, so use an ID from your own listing.
Caution
With a cross-Region profile, prompts and responses can leave your source Region. With a
global.profile, they can leave your geography. AWS also notes that any data it stores for abuse detection is stored in the destination Region. Check with your data owner before usingglobal.for regulated data. Requests can also route to opt-in Regions you haven't enabled.
Step 2: Fix the IAM policy
This is the most common follow-on failure. The role needs the profile ARN in the source Region and the model ARN in every destination Region. For a US profile called from us-east-1:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowProfile",
"Effect": "Allow",
"Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
"Resource": "arn:aws:bedrock:us-east-1:111122223333:inference-profile/us.anthropic.claude-sonnet-4-5-20250929-v1:0"
},
{
"Sid": "AllowModelOnlyThroughProfile",
"Effect": "Allow",
"Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
"Resource": [
"arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-5-20250929-v1:0",
"arn:aws:bedrock:us-east-2::foundation-model/anthropic.claude-sonnet-4-5-20250929-v1:0",
"arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-sonnet-4-5-20250929-v1:0"
],
"Condition": {
"StringEquals": {
"bedrock:InferenceProfileArn": "arn:aws:bedrock:us-east-1:111122223333:inference-profile/us.anthropic.claude-sonnet-4-5-20250929-v1:0"
}
}
}
]
}
The condition means the role can only reach the model through this profile. Replace the Regions with the ones get-inference-profile returned.
global. profiles need one more resource: a model ARN with no Region or account, arn:aws:bedrock:::foundation-model/<model-id>. AWS evaluates that resource with aws:RequestedRegion set to unspecified, so any Region condition in your policies has to allow that value. See the global cross-Region inference page in the Bedrock user guide for the full template.
Step 3: Check SCPs
AWS documents this behaviour: if any destination Region in a cross-Region profile is blocked by your SCPs, the request will fail even when other Regions are allowed. A Region allow-list containing only us-east-1 will break a US profile that routes to us-east-2 and us-west-2.
You have two options:
- Add the destination Regions to the allow-list. For
global.profiles, the allow-list also needsunspecified. - Keep the Region deny and exempt cross-Region routing with the
bedrock:InferenceProfileArncondition key. This can't bypass the restriction on the originating call, so the source Region must still be allowed.
Warning
A wrong edit to a Region SCP affects every account under that OU. Test on a sandbox OU first, and have your cloud platform team make the change. AWS strongly discourages manually editing SCPs managed by Control Tower.
Step 4: Change the app and test
Keep the ID in configuration so the next model change doesn't need a code release.
import os
import boto3
client = boto3.client("bedrock-runtime", region_name=os.environ["BEDROCK_REGION"])
response = client.converse(
modelId=os.environ["BEDROCK_MODEL_ID"], # a profile ID, not the plain model ID
messages=[{"role": "user", "content": [{"text": "ping"}]}],
inferenceConfig={"maxTokens": 10},
)
print(response["output"]["message"]["content"][0]["text"])
Test from the CLI first, as the app's role:
aws bedrock-runtime converse --region "$REGION" \
--model-id us.anthropic.claude-sonnet-4-5-20250929-v1:0 \
--messages '[{"role":"user","content":[{"text":"ping"}]}]' \
--inference-config maxTokens=10
Note
Inference profiles don't support Provisioned Throughput. If the app calls a Provisioned Throughput model ARN, leave it as it is, because this error doesn't apply to that path.
Step 5: Check what the switch changed
Quotas. Cross-Region inference has its own quota names, so a raise on the old quota may not carry over.
aws service-quotas list-service-quotas --region "$REGION" --service-code bedrock \
--query "Quotas[?contains(QuotaName, 'Cross-region')].[QuotaName,Value]" --output table
Where requests ran. CloudTrail records requests in the source Region, with the processing Region in additionalEventData.inferenceRegion:
aws cloudtrail lookup-events --region "$REGION" \
--lookup-attributes AttributeKey=EventSource,AttributeValue=bedrock.amazonaws.com \
--max-results 20 --query 'Events[].CloudTrailEvent' --output json \
| jq -r '.[] | fromjson | [.eventTime, .eventName, .additionalEventData.inferenceRegion] | @tsv'
Dashboards and alarms. Anything filtered on the old model ID can go quiet without any error. Check which ModelId values CloudWatch now reports, and update your alarms to match:
aws cloudwatch list-metrics --region "$REGION" --namespace AWS/Bedrock \
--metric-name Invocations --query "Metrics[].Dimensions[].Value" --output text
Cost. Pricing is based on the Region you call the profile from. Check current pricing for your profile type before rollout.
Prevent it next time
- Test model changes in a non-production account. It should have the same SCPs and role policies as production, so permission failures show up before release.
- Treat a model ID as config. Store the profile ID and Region together, because a profile that works from one Region may not exist in another.
- Generate IAM resources from the profile. Build the ARN list from
get-inference-profileinstead of copying Regions by hand. - Recheck
global.destinations. Geographic profile destinations stay fixed, but global destinations can change as AWS adds Regions. - Alert on
ValidationExceptionandAccessDeniedExceptionby model ID. A sudden rise right after a config change usually points to this problem. - Use an application inference profile for chargeback. Create it from the system-defined profile, not the plain model ID, or it fails with the same error.