AWS Bedrock Invalid Model ID — ValidationException Fix (2026) | AI Error Hub
AWS Bedrock Routing · Model ID Severity: Low HTTP 400

AWS Bedrock Invalid Model ID

Bedrock rejected the call before it even hit the model — ValidationException at the SDK boundary. Common causes: missing version suffix (:0), wrong provider prefix, using a foundation-model ARN where a plain ID is expected, or a copy-paste artifact from the console.

Provider: AWS Bedrock Category: Routing · Model ID First seen: 2023-09 (Bedrock GA) Last verified: Jul 22, 2026

Quick Fix (TL;DR)

TL;DR

Bedrock model IDs follow a strict format: {provider}.{model-name}-{version-date}-v{X}:{Y} — for example anthropic.claude-sonnet-4-6-20250929-v1:0. Missing the :0 suffix or the date component causes 90% of these failures. Use list_foundation_models() to fetch the exact canonical string, then match it byte-for-byte.

The Full Error Message

Missing version suffix
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the Converse operation: The provided model identifier is invalid. Valid model identifiers include 'anthropic.claude-sonnet-4-6-20250929-v1:0'.
Foundation-model ARN passed as modelId
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the InvokeModel operation: Malformed input request: #: expected type: String, found: Object. If you intended to use an ARN, pass the ARN string not an object.
Provider prefix typo
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the Converse operation: The provided model identifier is invalid. Did you mean 'anthropic.claude-haiku-4-5-20251001-v1:0'?

Bedrock Model ID Format Anatomy

Every foundation-model ID has five parts. Missing any of them yields ValidationException.

PartExampleNotes
Provider prefixanthropic.Lowercase, includes trailing dot
Model familyclaude-sonnetHyphen-separated tokens
Generation4-6Two-digit major.minor
Version date20250929YYYYMMDD, model release date
Version suffixv1:0Interface version and revision

Common ID Format Mistakes

The eight typos we see most often.

You wroteYou meantWhat Bedrock says
anthropic/claude-...anthropic.claude-...Slash not accepted, use dot
claude-sonnet-4-6anthropic.claude-sonnet-4-6-20250929-v1:0Missing provider prefix + date + version
anthropic.claude-sonnet-4-6-v1...20250929-v1:0Missing version date + revision
...-v1...-v1:0Missing revision after colon
Anthropic.Claude-Sonnet-4-6-...anthropic.claude-sonnet-4-6-...Case-sensitive, lowercase
arn:aws:bedrock:...anthropic.claude-...Pass ID string, not ARN (unless spec says ARN)
us.anthropic.claude-... as modelId in InvokeModelanthropic.claude-...Profile prefix only for Converse
anthropic.claude-instant-v1anthropic.claude-haiku-4-5-20251001-v1:0Deprecated — use replacement

Root Causes (Ranked by Frequency)

35%
Missing version suffix. Wrote anthropic.claude-sonnet-4-6-20250929 without the -v1:0. The colon-revision is mandatory.
25%
Missing version date. Wrote anthropic.claude-sonnet-4-6-v1:0. The YYYYMMDD component is required for all Anthropic models.
12%
Slash instead of dot after provider. Wrote anthropic/claude-sonnet-4-6-.... Bedrock uses dots, not slashes, as provider separators.
10%
Wrong provider prefix. amazon.claude-... or openai.gpt-... — provider must match; Amazon owns Nova/Titan, Anthropic owns Claude.
8%
Passing a foundation-model ARN where an ID is expected. InvokeModel takes a plain ID; some newer APIs accept both, but mixing them causes validation.
5%
Deprecated model ID. Legacy IDs like anthropic.claude-instant-v1 are validated but return a deprecation error suggesting the replacement.
3%
Case mismatch. Anthropic.Claude-... instead of lowercase — model IDs are case-sensitive.
2%
Trailing whitespace. Value from an env var or CSV has a trailing newline or space.

Fix #1 — Fetch the Canonical Model ID Programmatically

FIX 1Never hardcode — always read the exact string from Bedrock.

The safest way to get a model ID right is to fetch it from list_foundation_models() at deploy time. This script prints every model available to your account in the target region, formatted for copy-paste into config. Filter by provider or family to narrow down.

print_canonical_model_ids.py
import boto3
import argparse

def print_ids(region: str, provider_filter: str = None, family_filter: str = None):
    br = boto3.client("bedrock", region_name=region)
    resp = br.list_foundation_models()

    print(f"# Bedrock foundation models available in {region}")
    print(f"# Fetched via list_foundation_models()\n")

    for m in resp["modelSummaries"]:
        model_id = m["modelId"]

        if provider_filter and not model_id.startswith(f"{provider_filter}."):
            continue
        if family_filter and family_filter not in model_id:
            continue

        provider = m.get("providerName", "")
        name = m.get("modelName", "")
        streaming = "streaming" if m.get("responseStreamingSupported") else "sync-only"
        lifecycle = m.get("modelLifecycle", {}).get("status", "")

        print(f"{model_id}")
        print(f"    provider  = {provider}")
        print(f"    name      = {name}")
        print(f"    streaming = {streaming}")
        print(f"    lifecycle = {lifecycle}\n")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--region", default="us-east-1")
    parser.add_argument("--provider", help="Filter: anthropic, amazon, meta, mistral, cohere")
    parser.add_argument("--family", help="Substring filter: sonnet, opus, haiku, nova, etc.")
    args = parser.parse_args()
    print_ids(args.region, args.provider, args.family)

Usage: python print_canonical_model_ids.py --region us-east-1 --provider anthropic --family sonnet prints every Sonnet variant in us-east-1. Pipe to grep -oE '[a-z]+\.[a-z0-9\-]+:[0-9]+' to extract just the IDs.

Fix #2 — Validate Model IDs at Config-Load Time

FIX 2Reject malformed IDs before they hit production.

A tiny validator that checks the ID structure against Bedrock's known format. Wire it into your config loader (Pydantic, dataclasses, whatever) so an invalid ID fails at startup rather than at first user request.

validate_model_id.py
import re
from typing import Optional

# Provider.family-generation-YYYYMMDD-vX:Y
# Allow optional us./eu./apac. profile prefix.
_MODEL_ID_PATTERN = re.compile(
    r"^"
    r"(?:(?P<prefix>us|eu|apac)\.)?"          # optional profile prefix
    r"(?P<provider>[a-z0-9]+)\."               # provider (anthropic, amazon, meta, ...)
    r"(?P<family>[a-z0-9\-]+?)"                # family (claude-sonnet, nova-pro, ...)
    r"(?:-(?P<date>\d{8}))?"                   # optional YYYYMMDD version date
    r"-v(?P<major>\d+)"                        # interface version
    r"(?::(?P<minor>\d+))?"                    # optional revision
    r"$"
)

_KNOWN_PROVIDERS = {"anthropic", "amazon", "meta", "mistral", "cohere",
                    "ai21", "stability", "deepseek"}

def validate(model_id: str) -> tuple[bool, Optional[str]]:
    """Return (valid, error_reason). Reason is None when valid."""
    if not model_id:
        return False, "empty string"
    if model_id != model_id.strip():
        return False, "leading or trailing whitespace"
    if model_id != model_id.lower():
        return False, "must be lowercase"

    match = _MODEL_ID_PATTERN.match(model_id)
    if not match:
        return False, f"does not match pattern {_MODEL_ID_PATTERN.pattern}"

    provider = match.group("provider")
    if provider not in _KNOWN_PROVIDERS:
        return False, f"unknown provider '{provider}' (known: {_KNOWN_PROVIDERS})"

    # Anthropic requires the version date; Amazon does not for older Nova.
    if provider == "anthropic" and not match.group("date"):
        return False, "Anthropic models require YYYYMMDD version date"

    # Almost every model requires the :N revision suffix.
    if match.group("minor") is None:
        return False, "missing revision suffix (e.g. :0)"

    return True, None

# Self-test
if __name__ == "__main__":
    for candidate in [
        "anthropic.claude-sonnet-4-6-20250929-v1:0",  # valid
        "anthropic.claude-sonnet-4-6-v1:0",           # invalid: no date
        "anthropic.claude-sonnet-4-6-20250929-v1",    # invalid: no :0
        "Anthropic.Claude-Sonnet-4-6-20250929-v1:0",  # invalid: uppercase
        "us.anthropic.claude-opus-4-7-20260315-v1:0", # valid: profile prefix
        "amazon.nova-pro-v1:0",                        # valid: no date needed
    ]:
        ok, reason = validate(candidate)
        status = "OK" if ok else f"INVALID: {reason}"
        print(f"{candidate:<50} {status}")

Where to hook this: in Pydantic, add a @field_validator("model_id"). In dataclasses, add a __post_init__ check. In env-driven configs, validate at process start. The point is to fail fast on bad IDs, not at the moment a customer request hits the SDK.

Fix #3 — Handle ARN-vs-ID Confusion Explicitly

FIX 3Some APIs want IDs, some want ARNs, some accept either.

Bedrock has three flavors of "how do I refer to a model": plain ID (anthropic.claude-...), foundation-model ARN (arn:aws:bedrock:REGION::foundation-model/anthropic.claude-...), and inference-profile ID/ARN. This helper normalizes any of them to the format the specific API expects, so call sites don't have to remember the rules.

normalize_model_ref.py
from typing import Literal

def normalize(model_ref: str,
              target: Literal["id", "arn"],
              region: str = "us-east-1",
              account: str = None) -> str:
    """Convert between model ID and model ARN.

    Examples:
        normalize("anthropic.claude-sonnet-4-6-20250929-v1:0", target="arn")
        -> "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6-20250929-v1:0"

        normalize("arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-...", target="id")
        -> "anthropic.claude-..."
    """
    is_arn = model_ref.startswith("arn:aws:bedrock:")

    if target == "id":
        if not is_arn:
            return model_ref  # already an ID
        # Extract the last path segment after "foundation-model/" or "inference-profile/"
        return model_ref.rsplit("/", 1)[-1]

    elif target == "arn":
        if is_arn:
            return model_ref  # already an ARN
        # Detect profile vs foundation model
        if model_ref.startswith(("us.", "eu.", "apac.")):
            # Inference profile — needs the account ID.
            if not account:
                raise ValueError("inference-profile ARN requires account ID")
            return f"arn:aws:bedrock:{region}:{account}:inference-profile/{model_ref}"
        # Foundation model — no account in ARN.
        return f"arn:aws:bedrock:{region}::foundation-model/{model_ref}"

    raise ValueError(f"target must be 'id' or 'arn', got {target}")

# Quick self-check
if __name__ == "__main__":
    tests = [
        ("anthropic.claude-sonnet-4-6-20250929-v1:0", "arn", "us-east-1", None),
        ("arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-haiku-4-5-20251001-v1:0", "id", None, None),
        ("us.anthropic.claude-opus-4-7-20260315-v1:0", "arn", "us-east-1", "123456789012"),
    ]
    for ref, target, region, account in tests:
        result = normalize(ref, target, region=region or "us-east-1", account=account)
        print(f"{ref[:50]:<52} --{target}--> {result[:80]}")

Rule of thumb: InvokeModel and Converse accept both ID and ARN. Bedrock Agent and Knowledge Base configuration usually want ARNs. IAM policies always want ARNs in the Resource block. When in doubt, normalize to ARN for storage and to ID for logging.

Prevention Checklist

  • Never hardcode model IDs — pull from a config file or Secrets Manager entry that itself is validated at load time.
  • Version-pin the full model ID including -YYYYMMDD-v1:0 — using a family name like claude-sonnet is always wrong.
  • Run the validate_model_id.py regex at CI time against every model ID string in your codebase.
  • For copy-paste from AWS docs, verify the ID actually appears in list_foundation_models() — docs occasionally list preview IDs that aren't yet GA.
  • When the AWS blog announces a new model, update your config in a PR and run validation before merging.
  • Emit the model ID as a structured log field on every Bedrock invocation — makes debugging invalid-ID errors trivial.
  • For customer-facing model selection UIs, list only IDs returned by list_foundation_models() — don't let users type free-form IDs.

Tested by

Frequently Asked Questions

For Anthropic models, yes — all current Claude IDs include the release date and rejecting a call without it is guaranteed.

For Amazon Nova, older versions (Nova Pro v1) don't have a date; newer premium models do. Meta Llama 4 uses dates; Llama 3 sometimes doesn't.

When in doubt, include the date and test — if Bedrock accepts a date-less ID, both formats work for that model; if not, the date is mandatory.

Both work for InvokeModel and Converse.

The ID is shorter and more common in examples; the ARN is required in IAM policy Resource blocks and in some cross-account patterns.

If you're building an SDK wrapper, accept both and normalize internally. If you're writing straight application code, plain IDs are more readable.

Bedrock's validator does fuzzy matching on invalid IDs and suggests the closest valid one. If you write anthropic.claude-sonnet-4 and there's no such ID, the error might say "Did you mean anthropic.claude-sonnet-4-6-20250929-v1:0?"

This is a helpful hint but not authoritative — always confirm with list_foundation_models() rather than accepting the suggestion blindly, especially across account/region boundaries.

The v1:0 part has two components:

  • v1 is the interface/API version (change indicates breaking API changes)
  • :0 is the revision (increments for minor updates within the same interface)

Most current models are at v1:0. When AWS ships v2 of an interface, both v1 and v2 typically exist in parallel for at least 6 months to allow migration. Pin to a specific v{X}:{Y} to freeze behavior.

No. Bedrock does not support wildcards or aliases in model IDs. Every call must specify an exact model ID including the version date and revision.

This is intentional — AWS wants inference behavior to be reproducible, and a "latest" pointer would change without deployment. For automated model updating, use a resolver pattern like Fix #3 on the model-not-available-in-region page.

Related Errors

Never get blindsided by an AI provider error again.

Every Tuesday, get the week's newest AWS Bedrock, Anthropic, OpenAI, and Gemini errors — with tested fixes — delivered to your inbox. Free, no spam, unsubscribe with one click.

Last verified Jul 22, 2026 · AWS Bedrock is a trademark of Amazon Web Services · AI Error Hub is not affiliated with AWS.