Your ECS task will not start and the stopped task reports CannotPullContainerError. This is a pull-time failure: the ECS agent tried to fetch your container image from the registry and could not. Nothing about your application code is running yet, so the fix lives in one of three places: the image reference, the network path to the registry, or the execution role. This page shows how to read the stopped task and route to the correct fix.
What this error means
Before your container starts, the ECS agent authenticates to the registry, resolves the image reference, and downloads the layers. CannotPullContainerError means one of those steps failed and ECS gave up after retrying. The full stoppedReason on the task almost always names the real cause, for example an access denied response, an image not found response, or an i/o timeout that points at the network. Read that field first and let it choose your path.
Common causes
- The image or tag does not exist: a typo in the image URI, a tag that was never pushed, or a
latesttag that was overwritten or deleted. - No network route to the registry: a Fargate task in a private subnet with no NAT gateway and no VPC endpoints for ECR.
- An
awsvpctask placed in a public subnet with no public IP, so it has no path out at all. - Missing or wrong IAM: the execution role lacks
ecr:GetAuthorizationToken,ecr:BatchGetImage, orecr:GetDownloadUrlForLayer. - Pulling from Docker Hub and hitting anonymous rate limits, or needing registry credentials supplied through Secrets Manager.
- A KMS encrypted ECR repository where the execution role has no
kms:Decryptpermission.
How to fix it
Stabilize
First confirm the deploy is blocked and not flapping. Pin the service to a known good task definition revision so you stop launching failing tasks while you diagnose.
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--task-definition my-task:LAST_GOOD_REVISION
Diagnose
- Read the exact reason on a stopped task.
aws ecs describe-tasks \
--cluster my-cluster \
--tasks TASK_ID \
--query "tasks[0].{reason:stoppedReason,containers:containers[].reason}"
- If the reason mentions the manifest or resolving a ref, confirm the image and tag actually exist.
aws ecr describe-images \
--repository-name my-app \
--image-ids imageTag=v1.4.2
- If the reason is a timeout or a network failure, inspect the subnets and their routes.
aws ec2 describe-route-tables \
--filters Name=association.subnet-id,Values=subnet-abc123 \
--query "RouteTables[].Routes"
Fix
Apply the fix for the cause the reason pointed to.
Image or tag missing: correct the image value in the task definition to the full URI including the account, region, and a real tag, then register a new revision. Confirm the tag was pushed by CI.
No route to ECR: for a private subnet, add either a NAT gateway route or interface endpoints for com.amazonaws.REGION.ecr.api and com.amazonaws.REGION.ecr.dkr, a gateway endpoint for S3, and network access to CloudWatch Logs. For a public subnet task, set assignPublicIp to ENABLED in the network configuration.
IAM: attach a policy to the execution role with the pull actions.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
"Resource": "*"
}
]
}
Docker Hub credentials: store them in Secrets Manager and reference the secret in the container definition through repositoryCredentials, then grant the execution role secretsmanager:GetSecretValue on that secret. For a KMS encrypted repo, add kms:Decrypt on the key to the execution role.
How to prevent it
- Use immutable ECR tags so a tag can never be overwritten or silently deleted.
- Reference images by digest in production so the pull cannot resolve to a moved tag.
- Keep the execution role least privilege but complete: the three ECR actions, logs, and any secret or KMS access the pull needs.
- For private subnets, standardize on ECR, S3, and Logs VPC endpoints so pulls never depend on the public internet.
- Verify the push succeeded in CI before the deploy step runs.
When to call a senior engineer
If the stoppedReason points at the network and the route tables, endpoints, and security groups all look correct, the problem is usually a subtle VPC or endpoint policy interaction that is slow to isolate mid-incident. If your deploy is blocked and every obvious cause checks out, bring in someone who can read the VPC flow logs and endpoint policies with you rather than guessing under pressure.
Related errors we fix
AccessDenied: User is not authorized to perform this operation Fix this error → AWS Stack:arn:... is in ROLLBACK_COMPLETE state and can not be updated. Fix this error → AWS UPDATE_ROLLBACK_FAILED Fix this error → AWS ProvisionedThroughputExceededException: The level of configured provisioned throughput for the table was exceeded Fix this error → Want a senior engineer to just fix it?
Paste this exact error into the form. A senior engineer replies within one business hour with what is likely wrong and a flat quote. Triage is free.
Book a fix