Skip to content

[Bug]: Bedrock inference profile model ID dots mangled to hyphens → HTTP 400 "invalid model identifier" #11976

@seokjushin

Description

@seokjushin

Summary

When using a Bedrock inference profile (e.g. global.anthropic.claude-opus-4-7), the agent fails with:

HTTP 400: The provided model identifier is invalid.

Root cause: run_agent.py::AIAgent._anthropic_preserve_dots() does not include "bedrock" in its allowlist, so normalize_model_name() converts the structural . separators in the Bedrock inference-profile ID to -, producing global-anthropic-claude-opus-4-7, which Bedrock rejects.

This is the same bug class as #5211 (opencode-go), which was fixed in #5597 by adding opencode-go to the same allowlist. The PR #10549 that added native Bedrock support missed extending this allowlist to cover the new bedrock provider.

Environment

  • Hermes Agent v0.10.0 (2026.4.16)
  • Python 3.11.15, hermes-agent[bedrock,messaging] installed
  • AWS region: ap-northeast-2 (IMDS instance role auth)
  • Config:
    model:
      default: global.anthropic.claude-opus-4-7
      provider: bedrock
    bedrock:
      region: ap-northeast-2

hermes doctor confirms Bedrock is reachable (24 models discovered). A direct call with the same model ID via both boto3.client("bedrock-runtime").converse(...) and anthropic.AnthropicBedrock(aws_region=...).messages.create(model="global.anthropic.claude-opus-4-7", ...) succeeds. Only the hermes-gateway path fails.

Reproduction

  1. Install Hermes v0.10.0 with [bedrock].
  2. Set model.default: global.anthropic.claude-opus-4-7, model.provider: bedrock in ~/.hermes/config.yaml.
  3. Send any message through the Discord gateway.
  4. Observe HTTP 400: The provided model identifier is invalid. in ~/.hermes/logs/gateway.error.log.

The request-dump JSON (~/.hermes/sessions/request_dump_*.json) shows:

"body": {
  "model": "global-anthropic-claude-opus-4-7",   // <-- dots mangled to hyphens
  ...
}

Root Cause

run_agent.py (HEAD, around L6568):

def _anthropic_preserve_dots(self) -> bool:
    if (getattr(self, "provider", "") or "").lower() in {"alibaba", "minimax", "minimax-cn", "opencode-go", "opencode-zen", "zai"}:
        return True
    base = (getattr(self, "base_url", "") or "").lower()
    return "dashscope" in base or "aliyuncs" in base or "minimax" in base or "opencode.ai/zen/" in base or "bigmodel.cn" in base

bedrock is missing from both the provider allowlist and the base-URL heuristic. base_url for Bedrock is https://bedrock-runtime.<region>.amazonaws.com, which does not match any of the existing substrings either.

Suggested Fix

One-line change — add "bedrock" to the provider allowlist:

-    if (getattr(self, "provider", "") or "").lower() in {"alibaba", "minimax", "minimax-cn", "opencode-go", "opencode-zen", "zai"}:
+    if (getattr(self, "provider", "") or "").lower() in {"alibaba", "minimax", "minimax-cn", "opencode-go", "opencode-zen", "zai", "bedrock"}:

Optionally also add "bedrock-runtime.amazonaws.com" to the base_url heuristic for defense-in-depth.

I verified this fix resolves the issue on a production deployment (Discord gateway, Bedrock Opus 4.7, ap-northeast-2). Happy to open a PR if useful.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions