
Bedrockアプリケーション推論プロファイル利用時にClaude Codeのbuilt-in subagentが403エラーになる問題と対処法
はじめに
Claude Codeを企業に導入する際に、Bedrock経由で使うケースがあります。API利用料をAWSの請求に統合でき、通信先がAWSに閉じる点や、IAMによるアクセス制御・CloudTrailによる監査ログなど、既存のAWSの管理基盤に乗せられるメリットがあります。
さらに、個人ごとの利用料を把握したいというモチベーションが出てくると、アプリケーション推論プロファイルを作成してユーザー単位でトラッキングする構成が必要になります。
このユースケースで、Claude Codeで一部環境変数を設定しないとbuilt-inのsubagentが起動しないケースがありますので、問題詳細と解決方法をご紹介します。
なお、本記事はClaude Code 2.1.104 で確認しています。
Built-in subagents
Claude Codeの公式ドキュメントは以下なのでこちらをご覧ください。
/agents から tabを押して、Libraryをみると内容が確認できます。

私がよく使うのは claude-code-guide です。
Claude CodeにClaude Codeの設定をさせる場合かなり精度が悪いです。具体例を出すとClaude CodeのRulesやSkillをコードベースに初回作成しようとした場合、他のAIエージェントの仕様と混ざるのか、ハルシネーションし設定が読み込まれないケースがありました。そこで前述の claude-code-guide が有効です。
以下の画像のような形でsubagentsの呼び出しを確認することができます。

※ claude-code-guideを明示的に指定しているのは、自然に発火するケースが少ないためです 😭
呼び出されている中身をみると、subagentからそれっぽい内容が回答されていることが確認できます。

問題の事象
以下はアプリケーション推論プロファイルをメンバーごとに作成するCDKコードです。
config.ts: チーム名とメンバー(名前・メールアドレス)の一覧を定義main-stack.ts: configの定義をもとに、メンバーごとに以下のリソースを作成- アプリケーション推論プロファイル(クロスリージョン推論プロファイルをソースに指定)
- IAMユーザーとアクセスキー
- 認証情報をSecrets Managerに格納
設定ファイル(config.ts)
type Config = {
teams: {
name: string;
members: {
name: string;
email: string;
}[];
}[];
};
export const config: Config = {
teams: [
{
name: "olympus",
members: [
{ name: "zeus", email: "zeus@example.com" },
{ name: "athena", email: "athena@example.com" },
{ name: "apollo", email: "apollo@example.com" },
],
},
{
name: "asgard",
members: [
{ name: "thor", email: "thor@example.com" },
{ name: "freyja", email: "freyja@example.com" },
{ name: "baldur", email: "baldur@example.com" },
],
},
],
};
CDKスタック(main-stack.ts)
import * as bedrock from "@aws-cdk/aws-bedrock-alpha";
import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
import { pascalCase } from "change-case";
import type { Construct } from "constructs";
import { config } from "./config.js";
export class MainStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const model = bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_SONNET_4_6;
const cris = bedrock.CrossRegionInferenceProfile.fromConfig({
geoRegion: bedrock.CrossRegionInferenceProfileRegion.US,
model,
});
for (const team of config.teams) {
for (const member of team.members) {
const prefix = `${team.name}-${member.name}`;
const idPrefix = pascalCase(prefix);
const aip = new bedrock.ApplicationInferenceProfile(this, `${idPrefix}Aip`, {
applicationInferenceProfileName: `sonnet4-${team.name}-${member.name}`,
modelSource: cris,
description: `${team.name}-${member.name}`,
tags: {
team: team.name,
name: member.name,
email: member.email,
},
});
const user = new iam.User(this, `${idPrefix}User`, {
userName: `${prefix}-bedrock`,
});
aip.grantInvoke(user);
user.addToPolicy(
new iam.PolicyStatement({
actions: ["bedrock:InvokeModelWithResponseStream"],
resources: [aip.inferenceProfileArn],
}),
);
const accessKey = new iam.AccessKey(this, `${idPrefix}Key`, { user });
new secretsmanager.Secret(this, `${idPrefix}Creds`, {
description: `Bedrock API creds for ${team.name}/${member.name}`,
secretObjectValue: {
AccessKeyId: cdk.SecretValue.unsafePlainText(accessKey.accessKeyId),
SecretAccessKey: accessKey.secretAccessKey,
},
});
}
}
}
}
export PROFILE_NAME="sonnet4-olympus-zeus"
export ARN=$(aws bedrock list-inference-profiles --region us-west-2 --type-equals APPLICATION --query "inferenceProfileSummaries[?inferenceProfileName=='${PROFILE_NAME}'].inferenceProfileArn" --output text)
export MEMBER_KEY=$(echo "${PROFILE_NAME#sonnet4-}" | awk -F- '{for(i=1;i<=NF;i++) printf toupper(substr($i,1,1)) substr($i,2)}')
export SECRET_ID=$(aws secretsmanager list-secrets --region us-west-2 --query "SecretList[?starts_with(Name, '${MEMBER_KEY}Creds')].Name" --output text)
export CREDS=$(aws secretsmanager get-secret-value --secret-id "${SECRET_ID}" --region us-west-2 --query SecretString --output text)
echo "export AWS_ACCESS_KEY_ID=$(echo "${CREDS}" | jq -r .AccessKeyId)"
echo "export AWS_SECRET_ACCESS_KEY=$(echo "${CREDS}" | jq -r .SecretAccessKey)"
echo "export AWS_REGION=us-west-2"
echo "export CLAUDE_CODE_USE_BEDROCK=1"
echo "export ANTHROPIC_MODEL=${ARN}"
echo "export ANTHROPIC_SMALL_FAST_MODEL=${ARN}"
echo "export ANTHROPIC_DEFAULT_HAIKU_MODEL=${ARN}"
echo "export ANTHROPIC_DEFAULT_SONNET_MODEL=${ARN}"
echo "export ANTHROPIC_DEFAULT_OPUS_MODEL=${ARN}"
上記の出力からANTHROPIC_MODELまでの環境変数を設定してClaude Codeを起動します。
export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=yyy
export AWS_REGION=us-west-2
export CLAUDE_CODE_USE_BEDROCK=1
export ANTHROPIC_MODEL=arn:aws:bedrock:us-west-2:111111111111:application-inference-profile/ffffffffffff
テストメッセージを送信し、アプリケーション推論プロファイルが動作していることが確認できます。

claude-code-guideを使ってみます。
回答は返ってきましたが、subagent呼び出しでエラーになっていることがわかります。

Ctrl-oで内容を展開するとsubagentがAPI呼び出しをして403エラーになっていることが分かります。それだけでなくこのsubagentの回答を待つのに3分かかっています。バックグラウンドに回すことも可能ですが、ユーザーがコマンドを押さない限り無駄な時間を過ごすことになります。エラーになったことは回答として返ってくるので、気づかず時間を溶かしていたということはなさそうです。

この原因は、subagentが特定モデルに固定されているためだと考えられます。画像の通りclaude-code-guideはhaikuです。後述する環境変数を設定していないとsubagentは、haikuのシステム定義推論プロファイルを呼び出してしまい、権限がなくエラーになっているものと推測されます。

解決方法
ANTHROPIC_SMALL_FAST_MODEL、ANTHROPIC_DEFAULT_HAIKU_MODEL、ANTHROPIC_DEFAULT_SONNET_MODEL、ANTHROPIC_DEFAULT_OPUS_MODEL にもアプリケーション推論プロファイルのARNを設定します。
export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=yyy
export AWS_REGION=us-west-2
export CLAUDE_CODE_USE_BEDROCK=1
export ANTHROPIC_MODEL=arn:aws:bedrock:us-west-2:111111111111:application-inference-profile/ffffffffffff
export ANTHROPIC_SMALL_FAST_MODEL=arn:aws:bedrock:us-west-2:111111111111:application-inference-profile/ffffffffffff
export ANTHROPIC_DEFAULT_HAIKU_MODEL=arn:aws:bedrock:us-west-2:111111111111:application-inference-profile/ffffffffffff
export ANTHROPIC_DEFAULT_SONNET_MODEL=arn:aws:bedrock:us-west-2:111111111111:application-inference-profile/ffffffffffff
export ANTHROPIC_DEFAULT_OPUS_MODEL=arn:aws:bedrock:us-west-2:111111111111:application-inference-profile/ffffffffffff
claude-code-guide が正しく完了していることがわかります🥳🥳🥳

最後に
かなり重箱の隅をつつくネタですが、ご支援先でこの事象に遭遇したので記事にしました。この記事を書く過程でCLAUDE_CODE_USE_BEDROCK=1にすれば、デフォルトでシステム定義プロファイルにフォールバックされるようになっていることも確認しました。CloudShellからCLAUDE_CODE_USE_BEDROCK=1設定だけでClaude Codeが呼び出せるなど、Bedrock連携周りが充実してきたなと感じました。インストールもワンコマンドでバイナリですしね。。何か良いユースケースがあれば教えてください😆😆😆









