エラーの概要

AWSの409(Conflict)エラーは、リクエストの内容がAWSリソースの現在の状態と競合していることを示します。このエラーはS3、EC2、DynamoDB、CloudFormation、IAMなど複数のAWSサービスで発生し、リソースが完了していない状態遷移中であったり、既に同じ名前のリソースが存在していたりするときに返されます。一時的な問題か永続的な設定ミスかを判別することが解決の第一歩となります。

実際のエラーメッセージ例

S3バケット作成時:

{
  "Error": {
    "Code": "BucketAlreadyExists",
    "Message": "The requested bucket name is not available. The bucket namespace is shared by all AWS accounts."
  },
  "ResponseMetadata": {
    "HTTPStatusCode": 409
  }
}

EC2インスタンス操作時:

{
  "Error": {
    "Code": "InvalidInstanceID.Transitional",
    "Message": "The instance ID 'i-1234567890abcdef0' is in a transitional state and cannot be modified at this time."
  },
  "ResponseMetadata": {
    "HTTPStatusCode": 409
  }
}

DynamoDBテーブル操作時:

{
  "Error": {
    "Code": "ResourceInUseException",
    "Message": "Cannot update a table while an update is in progress"
  },
  "ResponseMetadata": {
    "HTTPStatusCode": 409
  }
}

よくある原因と解決手順

原因1:S3バケット名がグローバルに重複している

S3のバケット名はAWS全体で一意である必要があります。既に別のAWSアカウントが使用しているバケット名を指定すると409エラーが発生します。

Before(エラーが起きるコード):

import boto3

s3_client = boto3.client('s3')

# 既存の名前でバケットを作成しようとする
response = s3_client.create_bucket(
    Bucket='my-application-data'  # グローバルに重複している可能性
)

After(修正後):

import boto3
import uuid

s3_client = boto3.client('s3')

# タイムスタンプやUUIDでユニークな名前を生成
unique_bucket_name = f'my-application-data-{str(uuid.uuid4())[:8]}'

response = s3_client.create_bucket(
    Bucket=unique_bucket_name
)
print(f"Bucket created: {unique_bucket_name}")

原因2:リソースが状態遷移中である

EC2インスタンス、RDSインスタンス、DynamoDBテーブルなどは、起動・停止・更新中など状態遷移の途中では変更・削除操作ができません。この状態で操作を試みると409エラーが発生します。

Before(エラーが起きるコード):

import boto3

ec2_client = boto3.client('ec2')

# インスタンスの起動直後に停止を試みる
instance_id = 'i-1234567890abcdef0'

ec2_client.start_instances(InstanceIds=[instance_id])

# 起動完了を待たずに停止を指令
ec2_client.stop_instances(InstanceIds=[instance_id])

After(修正後):

import boto3
import time

ec2_client = boto3.client('ec2')
instance_id = 'i-1234567890abcdef0'

# インスタンスを起動
ec2_client.start_instances(InstanceIds=[instance_id])

# ウェイター機能を使用して起動完了まで待機
waiter = ec2_client.get_waiter('instance_running')
waiter.wait(InstanceIds=[instance_id])

print("Instance is running")

# その後に停止操作を実行
ec2_client.stop_instances(InstanceIds=[instance_id])

原因3:CloudFormationスタックが更新中である

CloudFormationスタックの作成・更新・削除中に、同じスタックに対して新たな操作を実行しようとすると409エラーが返されます。

Before(エラーが起きるコード):

import boto3

cloudformation_client = boto3.client('cloudformation')
stack_name = 'my-application-stack'

# スタックを更新
cloudformation_client.update_stack(
    StackName=stack_name,
    TemplateBody='...'
)

# 更新完了を待たずに別の更新を試みる
cloudformation_client.update_stack(
    StackName=stack_name,
    TemplateBody='...'
)

After(修正後):

import boto3

cloudformation_client = boto3.client('cloudformation')
stack_name = 'my-application-stack'

# スタックを更新
cloudformation_client.update_stack(
    StackName=stack_name,
    TemplateBody='...'
)

# ウェイターでスタック更新の完了を待機
waiter = cloudformation_client.get_waiter('stack_update_complete')
try:
    waiter.wait(StackName=stack_name)
    print("Stack update completed")
except Exception as e:
    print(f"Stack update failed: {e}")

# 更新完了後に次の操作を実行
cloudformation_client.update_stack(
    StackName=stack_name,
    TemplateBody='...'
)

原因4:DynamoDBの同時更新操作

DynamoDBテーブルのスケーリングや属性定義の変更中に、別の更新操作を試みると409エラーが発生します。

Before(エラーが起きるコード):

import boto3

dynamodb_client = boto3.client('dynamodb')
table_name = 'my-table'

# テーブルのキャパシティをスケーリング
dynamodb_client.update_table(
    TableName=table_name,
    BillingMode='PROVISIONED',
    ProvisionedThroughput={
        'ReadCapacityUnits': 100,
        'WriteCapacityUnits': 100
    }
)

# スケーリング完了を待たずにグローバルセカンダリインデックスを追加
dynamodb_client.update_table(
    TableName=table_name,
    AttributeDefinitions=[
        {'AttributeName': 'id', 'AttributeType': 'S'},
        {'AttributeName': 'gsi_key', 'AttributeType': 'S'}
    ],
    GlobalSecondaryIndexUpdates=[...]
)

After(修正後):

import boto3
import time

dynamodb_client = boto3.client('dynamodb')
table_name = 'my-table'

# テーブルのキャパシティをスケーリング
dynamodb_client.update_table(
    TableName=table_name,
    BillingMode='PROVISIONED',
    ProvisionedThroughput={
        'ReadCapacityUnits': 100,
        'WriteCapacityUnits': 100
    }
)

# テーブルが ACTIVE 状態になるまで待機
waiter = dynamodb_client.get_waiter('table_exists')
waiter.wait(TableName=table_name)

# ステータスを確認して確実にアクティブか確認
table_status = 'UPDATING'
while table_status != 'ACTIVE':
    response = dynamodb_client.describe_table(TableName=table_name)
    table_status = response['Table']['TableStatus']
    if table_status != 'ACTIVE':
        time.sleep(5)

# その後にグローバルセカンダリインデックスを追加
dynamodb_client.update_table(
    TableName=table_name,
    AttributeDefinitions=[
        {'AttributeName': 'id', 'AttributeType': 'S'},
        {'AttributeName': 'gsi_key', 'AttributeType': 'S'}
    ],
    GlobalSecondaryIndexUpdates=[...]
)

ツール固有の注意点

S3固有の注意点

S3では「BucketAlreadyExists」と「BucketAlreadyOwnedByYou」が区別されます。前者は別アカウントが所有していることを示し、後者は自分のアカウントで既に所有していることを示します。後者の場合は再取得または既存バケットの利用を検討してください。

また、バケット削除直後に同じ名前で再作成を試みると409エラーが発生することがあります。これはS3の最終一貫性モデルによるもので、数分待機してから再作成してください。

EC2固有の注目点

EC2インスタンスの状態遷移には、pendingrunningstoppingstopped といった複数の中間状態があります。AWS CLIaws ec2 describe-instances を実行して State.Name フィールドを確認し、running または stopped などの安定状態にあることを確認してから操作を進めてください。

セキュリティグループやネットワークインターフェイスの削除時も注意が必要です。これらがアクティブなインスタンスに関連付けられている場合、削除操作は409エラーで拒否されます。

IAM固有の注目点

IAMロールの信頼ポリシー(assume role policy)を変更中に、そのロールを新たなプリンシパルがassume(引き受ける)しようとすると409エラーが発生する場合があります。ポリシー変更後、十分な待機時間を設けてからロールを使用してください。

CloudFormation固有の注目点

CloudFormationスタックの「ROLLBACK_IN_PROGRESS」状態では、新たなスタック操作が受け付けられません。この場合、スタックが「ROLLBACK_COMPLETE」状態になるまで待機してから、必要に応じてスタック削除後に再作成してください。

それでも解決しない場合

ステップ1:リソースの状態を確認

AWS管理コンソールまたはAWS CLIでリソースの現在の状態を確認してください。EC2インスタンスやDynamoDBテーブルの状態遷移が完了しているかどうかを確認することが重要です。

# EC2インスタンスの状態確認
aws ec2 describe-instances --instance-ids <instance-id> --query 'Reservations[0].Instances[0].State.Name' --output text

# DynamoDBテーブルの状態確認
aws dynamodb describe-table --table-name <table-name> --query 'Table.TableStatus' --output text

# CloudFormationスタックの状態確認
aws cloudformation describe-stacks --stack-name <stack-name> --query 'Stacks[0].StackStatus' --output text

ステップ2:CloudTrailでAPI呼び出しログを確認

409エラーの詳細な原因を特定するため、AWS CloudTrailで対象のAPI呼び出しログを確認してください。CloudTrailは ~/.aws/ ディレクトリ以下に設定がある場合、AWS管理コンソールから「CloudTrail」サービスを開き、イベント履歴を検索することで詳細なエラーメッセージを確認できます。

ステップ3:公式ドキュメントとコミュニティリソース

AWSサービスの公式ドキュメントで、409エラーの詳細説明を確認してください。例えば:

AWS公式フォーラムやStack Overflowでエラーコードを検索し、他のユーザーの解決事例を参照することも有効です。


免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアの仕様は予告なく変更されることがあります。最新の情報は各ツールの公式サポートページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。