AWS `CannotPullContainerError`: What It Means and How to Fix It
CannotPullContainerError is an ECS task that cannot fetch its image — networking about half the time, IAM the other half. It comes from ECS, and the message reads:
CannotPullContainerError: pull image manifest has been retried … / pull access denied
If you are here mid-incident, skip to how to tell which cause you have — the command there narrows it down faster than reading the list.
Quick reference
| Error | CannotPullContainerError |
| Service | ECS |
| Message | CannotPullContainerError: pull image manifest has been retried … / pull access denied |
| Most common cause | A Fargate task in a private subnet with no route to ECR |
What it actually means
The task was placed successfully and then failed before starting because the agent could not pull the image. The specific wording after the colon tells you which half of the problem you have:
- “i/o timeout” or “context deadline exceeded” — a networking problem. The task cannot reach the registry at all.
- “pull access denied” or “no basic auth credentials” — an authorisation problem. It reached the registry and was refused.
- “manifest unknown” — the image or tag genuinely does not exist.
Diagnosing the wrong half wastes the most time here, so read the suffix first.
Why it happens
Ranked by how often each one turns out to be the answer.
| # | Cause | Fix |
|---|---|---|
| 1 | A Fargate task in a private subnet with no route to ECR | The commonest cause by far. A private subnet needs either a NAT gateway or three VPC endpoints: ecr.api, ecr.dkr, and an S3 gateway endpoint — image layers live in S3, so the first two alone are not enough. The ecr.dkr endpoint also needs Enable Private DNS Name selected, and the VPC needs enableDnsSupport and enableDnsHostnames, or the registry hostname still resolves to the public endpoint. |
| 2 | assignPublicIp disabled on a public subnet |
A Fargate task in a public subnet with no public IP has no internet path at all. Either enable it or use endpoints. |
| 3 | The execution role lacks ECR permissions | The execution role pulls the image; the task role is what your application code uses. They are different roles and this is the single most common confusion in ECS. Attach AmazonECSTaskExecutionRolePolicy to the execution role. |
| 4 | A cross-account ECR repository without a repository policy | Identity permissions in the pulling account are not enough — the repository policy in the owning account must also allow it. |
| 5 | Docker Hub rate limiting | Anonymous pulls from Docker Hub are rate-limited per IP, and a NAT gateway makes your whole VPC one IP. Use an ECR pull-through cache. |
| 6 | The tag does not exist | A build that pushed :latest while the task definition pins :v1.2.3. |
How to tell which one you have
# The full reason, which is truncated in the console.
aws ecs describe-tasks --cluster my-cluster --tasks <task-id> \
--query 'tasks[].containers[].[name,reason]' --output text
# Does the image and tag exist?
aws ecr describe-images --repository-name my-app \
--image-ids imageTag=v1.2.3
# Are the three endpoints present?
aws ec2 describe-vpc-endpoints --filters Name=vpc-id,Values=vpc-0123456789abcdef0 \
--query 'VpcEndpoints[].ServiceName' --output text
Look for ecr.api, ecr.dkr, and s3 in that last output. A missing s3 gateway endpoint with the other two present produces a pull that starts and then times out partway — a distinctive and confusing signature.
Preventing it
Standardise the VPC pattern: private subnets get all three ECR-related endpoints or a NAT gateway, never a partial set. Codify it in the module that creates VPCs so it cannot be forgotten.
Pin image tags to immutable digests in production task definitions, and enable ECR tag immutability so a rebuilt tag cannot silently change under a running service.
Related errors
ResourceInitializationError— ECS FargateAccessDenied vs AccessDeniedException— IAM- All AWS error references — the full index
Quota codes, limits and behaviour on this page were last checked against AWS documentation on 2026-08-22. AWS changes these; if something here does not match what you are seeing, trust the console and tell us.