Skip to content

Bug: AWS Bedrock fails in Asia Pacific regions with invalid model identifier error #12824

@datanerdie

Description

@datanerdie

Bug: AWS Bedrock fails in Asia Pacific regions with "invalid model identifier" error

Description

When using OpenCode with AWS Bedrock in Asia Pacific regions (e.g., ap-southeast-2), OpenCode attempts to use EU inference profile models (eu.anthropic.claude-haiku-4-5-20251001-v1:0) which don't exist in these regions, causing "The provided model identifier is invalid" errors.

Environment

  • OpenCode Version: Latest (main branch)
  • Region: ap-southeast-2 (Sydney, Australia)
  • AWS Profile: Configured with valid credentials
  • Provider: amazon-bedrock
  • Configuration:
    {
      "provider": {
        "amazon-bedrock": {
          "options": {
            "region": "ap-southeast-2",
            "profile": "bedrock-user"
          }
        }
      }
    }

Steps to Reproduce

  1. Configure AWS Bedrock provider with ap-southeast-2 region
  2. Configure oh-my-opencode agents to use Bedrock models:
    {
      "agents": {
        "build": {
          "model": "amazon-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0"
        }
      }
    }
  3. Run any OpenCode command that triggers the title generation agent
  4. Observe error logs showing EU model being used

Expected Behavior

OpenCode should:

  1. Detect the ap-southeast-2 region
  2. Select appropriate models for that region:
    • Direct models: anthropic.claude-haiku-4-5-20251001-v1:0
    • Global profiles: global.anthropic.claude-haiku-4-5-20251001-v1:0
    • AU profiles: au.anthropic.claude-haiku-4-5-20251001-v1:0 (for Australia)

Actual Behavior

OpenCode incorrectly selects EU inference profile models:

ERROR service=llm providerID=amazon-bedrock modelID=eu.anthropic.claude-haiku-4-5-20251001-v1:0
error={"error":{"statusCode":400,"responseBody":"{\"message\":\"The provided model identifier is invalid.\"}"}}

The API correctly calls the ap-southeast-2 endpoint but with an invalid EU model ID:

https://bedrock-runtime.ap-southeast-2.amazonaws.com/model/eu.anthropic.claude-haiku-4-5-20251001-v1%3A0/converse-stream

Root Cause

The bug is in the getSmallModel() function in /packages/opencode/src/provider/provider.ts (lines 1174-1195).

Problem 1: Incomplete Prefix List (Line 1175)

const crossRegionPrefixes = ["global.", "us.", "eu."]  // ❌ Missing "au.", "apac.", "jp."

This doesn't match the complete list used in getModel() at line 243:

const crossRegionPrefixes = ["global.", "us.", "eu.", "jp.", "apac.", "au."]  // ✅

Problem 2: Region Check Only for US/EU (Line 1188)

if (regionPrefix === "us" || regionPrefix === "eu") {  // ❌ Ignores all other regions!
  const regionalMatch = candidates.find((m) => m.startsWith(`${regionPrefix}.`))
  if (regionalMatch) return getModel(providerID, regionalMatch)
}

For ap-southeast-2:

  • regionPrefix = "ap" (from region.split("-")[0])
  • Condition evaluates to false
  • Regional model selection is completely skipped

Proposed Fix

Option 1: Add AP Region Support (Recommended)

if (providerID === "amazon-bedrock") {
  const crossRegionPrefixes = ["global.", "us.", "eu.", "jp.", "apac.", "au."]  // Add missing prefixes
  const candidates = Object.keys(provider.models).filter((m) => m.includes(item))

  // 1. Try global prefix (works everywhere)
  const globalMatch = candidates.find((m) => m.startsWith("global."))
  if (globalMatch) return getModel(providerID, globalMatch)

  // 2. Try regional prefix
  const region = provider.options?.region
  if (region) {
    let regionPrefix = region.split("-")[0]

    // Map region prefixes to inference profile prefixes
    if (regionPrefix === "ap") {
      if (["ap-southeast-2", "ap-southeast-4"].includes(region)) {
        regionPrefix = "au"  // Australia regions
      } else if (region === "ap-northeast-1") {
        regionPrefix = "jp"  // Tokyo region
      } else {
        regionPrefix = "apac"  // Other APAC regions
      }
    }

    if (["us", "eu", "au", "jp", "apac"].includes(regionPrefix)) {
      const regionalMatch = candidates.find((m) => m.startsWith(`${regionPrefix}.`))
      if (regionalMatch) return getModel(providerID, regionalMatch)
    }
  }

  // 3. Try unprefixed model
  const unprefixed = candidates.find((m) => !crossRegionPrefixes.some((p) => m.startsWith(p)))
  if (unprefixed) return getModel(providerID, unprefixed)
}

Option 2: Fallback to Global for Unsupported Regions (Simpler)

if (regionPrefix === "us" || regionPrefix === "eu") {
  const regionalMatch = candidates.find((m) => m.startsWith(`${regionPrefix}.`))
  if (regionalMatch) return getModel(providerID, regionalMatch)
} else {
  // For other regions, prefer global profiles
  const globalMatch = candidates.find((m) => m.startsWith("global."))
  if (globalMatch) return getModel(providerID, globalMatch)
}

Additional Context

  • The main getModel() function (lines 240-320) correctly handles AP regions with proper au., jp., and apac. prefix mapping
  • The inconsistency is only in getSmallModel() used for background tasks like title generation
  • All AWS Bedrock tests in /test/provider/amazon-bedrock.test.ts use only us-east-1 and eu-west-1 regions, which is why this bug wasn't caught

Impact

  • Affected regions: All Asia Pacific regions (ap-*), Japan (ap-northeast-1), and potentially other non-US/EU regions
  • Affected users: Anyone using AWS Bedrock outside US/EU regions
  • Workaround: Remove oh-my-opencode plugin (OpenCode falls back to Anthropic API)

Test Coverage Suggestion

Add test cases for AP regions in amazon-bedrock.test.ts:

test("Bedrock: getSmallModel works in ap-southeast-2 region", async () => {
  await using tmp = await tmpdir({
    init: async (dir) => {
      await Bun.write(
        path.join(dir, "opencode.json"),
        JSON.stringify({
          $schema: "https://opencode.ai/config.json",
          provider: {
            "amazon-bedrock": {
              options: {
                region: "ap-southeast-2",
              },
            },
          },
        }),
      )
    },
  })
  await Instance.provide({
    directory: tmp.path,
    init: async () => {
      Env.set("AWS_PROFILE", "default")
    },
    fn: async () => {
      const smallModel = await Provider.getSmallModel("amazon-bedrock")
      expect(smallModel).toBeDefined()
      // Should use global. or au. prefix, NOT eu.
      expect(smallModel?.id).toMatch(/^(global\.|au\.|anthropic\.)/)
      expect(smallModel?.id).not.toMatch(/^eu\./)
    },
  })
})

Related Code

  • Bug location: /packages/opencode/src/provider/provider.ts:1147-1211 (getSmallModel())
  • Working reference: /packages/opencode/src/provider/provider.ts:240-320 (getModel() - correctly handles AP regions)
  • Tests: /packages/opencode/test/provider/amazon-bedrock.test.ts (missing AP region test coverage)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions