This error occurs when Docker receives an HTML response instead of the expected JSON from a registry. Common causes include network proxies intercepting requests, corporate firewalls, geographic IP blocking, or misconfigured registry URLs.
This error indicates that Docker expected to receive a JSON response from a container registry but instead received an HTML page. When Docker communicates with registries (like Docker Hub, GitHub Container Registry, or private registries), it expects JSON-formatted responses containing image metadata and manifests. The key symptom is the `invalid character '<'` message - this `<` character is the start of an HTML document (like `<!DOCTYPE html>` or `<html>`). Docker's JSON parser encounters this HTML and fails because HTML is not valid JSON. This typically happens when something intercepts the request before it reaches the actual registry API. Instead of the registry responding, an intermediary returns an HTML page - often an error page (403 Forbidden, 404 Not Found), a captive portal login page, or a proxy authentication page. Docker cannot interpret this HTML response, hence the parsing error. The error commonly appears in these variations: - `error parsing HTTP 403 response body: invalid character '<' looking for beginning of value` - `error parsing HTTP 404 response body: invalid character '<' looking for beginning of value` - `Error response from daemon: invalid character '<' looking for beginning of value` - `error unmarshalling content: invalid character '<' looking for beginning of value`
The first step is to identify what's actually responding to Docker's requests. Use curl to test the registry endpoint directly:
# Test Docker Hub registry
curl -v https://registry-1.docker.io/v2/
# Test GitHub Container Registry
curl -v https://ghcr.io/v2/
# Test a private registry
curl -v https://your-registry.example.com/v2/If you see HTML in the response (look for <!DOCTYPE or <html>), that confirms something is intercepting the request. A healthy registry returns JSON like:
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required"}]}Note what you receive - is it a corporate firewall block page, a proxy authentication page, or a 404 error page?
If you're behind a corporate proxy, Docker needs to be configured to use it:
# Create Docker service override directory
sudo mkdir -p /etc/systemd/system/docker.service.d
# Create proxy configuration file
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf << EOF
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:8080"
Environment="HTTPS_PROXY=http://proxy.example.com:8080"
Environment="NO_PROXY=localhost,127.0.0.1,*.internal.company.com"
EOF
# Reload and restart Docker
sudo systemctl daemon-reload
sudo systemctl restart docker
# Verify proxy settings are applied
docker info | grep -i proxyFor Docker Desktop on Windows/Mac, configure proxy settings in:
- Docker Desktop > Settings > Resources > Proxies
Make sure to add any internal registries to NO_PROXY to bypass the proxy for internal traffic.
A simple typo in the registry URL can cause this error. Double-check your image references:
# Common typos that cause this error:
# Wrong: gchr.io (missing 'h')
# Right: ghcr.io
# Wrong: docker.pkg.github.com (deprecated)
# Right: ghcr.io
# Verify you can reach the correct registry
docker pull ghcr.io/owner/image:tag
# For Docker Hub images, the full path is:
docker pull docker.io/library/nginx:latest
# Or simply:
docker pull nginx:latestIf using a private registry, verify the hostname is correct and resolves properly:
nslookup your-registry.example.com
ping your-registry.example.comStale or corrupted credentials can cause authentication failures that result in HTML error pages:
# Log out from all registries
docker logout
docker logout ghcr.io
docker logout your-registry.example.com
# Log back in to Docker Hub
docker login
# Log in to GitHub Container Registry
echo $GITHUB_PAT | docker login ghcr.io -u USERNAME --password-stdin
# Log in to a private registry
docker login your-registry.example.comAlso check your Docker config file for stale entries:
# View current auth configuration
cat ~/.docker/config.json
# If you see corrupted or old entries, you can remove them:
# Edit ~/.docker/config.json and remove problematic auth entriesDocker Hub blocks access from certain countries due to US export control regulations. If you're in Cuba, Iran, North Korea, Crimea, Sudan, or Syria, you'll receive a 403 response.
To verify if this is the issue:
# Check your public IP
curl ifconfig.me
# Test with a VPN from an allowed region
# Enable VPN, then:
docker pull nginx:latestWorkarounds:
1. Use a VPN service
2. Use a Docker registry mirror (if available in your region)
3. Set up a private registry and manually transfer images
If you're on a network with a captive portal (hotel WiFi, airport, conference center), you need to authenticate through it first:
1. Open a web browser and try to visit any HTTP site (not HTTPS)
2. The captive portal should redirect you to a login page
3. Complete the authentication
4. Retry Docker commands
To check if a captive portal is the issue:
# Try to access a plain HTTP site
curl -v http://example.com
# If you see a redirect to a login page in the response,
# you have a captive portal issueAfter authenticating, verify connectivity:
curl -v https://registry-1.docker.io/v2/If using a private registry with self-signed or corporate CA certificates:
# Create certificate directory for the registry
sudo mkdir -p /etc/docker/certs.d/your-registry.example.com:5000
# Copy the CA certificate
sudo cp ca.crt /etc/docker/certs.d/your-registry.example.com:5000/
# If using client certificates, also copy:
sudo cp client.cert /etc/docker/certs.d/your-registry.example.com:5000/
sudo cp client.key /etc/docker/certs.d/your-registry.example.com:5000/
# Restart Docker
sudo systemctl restart dockerAlternatively, for testing only (not recommended for production), you can add the registry as insecure:
# Edit /etc/docker/daemon.json
{
"insecure-registries": ["your-registry.example.com:5000"]
}
# Restart Docker
sudo systemctl restart dockerIn some cases, BuildKit can have compatibility issues with certain registries:
# Disable BuildKit for a single build
DOCKER_BUILDKIT=0 docker build -t myimage .
# Or disable BuildKit globally in daemon.json
# Edit /etc/docker/daemon.json:
{
"features": {
"buildkit": false
}
}
# Restart Docker
sudo systemctl restart dockerIf disabling BuildKit resolves the issue, consider:
1. Updating Docker to the latest version
2. Checking if your registry supports BuildKit's manifest format
3. Opening an issue with the registry maintainers
If the issue is caused by corporate network policies:
1. Request whitelist: Ask IT to whitelist Docker registry domains:
- registry-1.docker.io
- auth.docker.io
- production.cloudflare.docker.com
- ghcr.io
- *.pkg.github.com
2. Request SSL inspection bypass: If your company uses SSL inspection, request that Docker registry traffic be exempted
3. Use a different network: As a temporary workaround, use a personal mobile hotspot or different network
4. Set up an internal registry mirror: Have IT set up an internal Docker registry that mirrors needed images:
# Configure Docker to use internal mirror
# Edit /etc/docker/daemon.json:
{
"registry-mirrors": ["https://internal-mirror.company.com"]
}### Understanding the Error in Detail
When Docker communicates with a registry, it uses the Docker Registry HTTP API V2. All responses should be JSON-formatted. The error occurs in Docker's Go JSON unmarshaller when it encounters unexpected characters.
The specific error invalid character '<' looking for beginning of value means:
- Docker tried to parse a JSON response
- The first character was < instead of {, [, ", or a JSON primitive
- This < is typically the start of <!DOCTYPE html> or <html>
### Debugging with Verbose Output
Enable verbose/debug output to see exactly what's happening:
# Enable Docker CLI debug mode
export DOCKER_CLI_DEBUG=1
docker pull nginx:latest
# Or check Docker daemon logs
sudo journalctl -u docker.service -f
# On Docker Desktop, enable debug mode in settings
# Then view logs at:
# macOS: ~/Library/Containers/com.docker.docker/Data/log/
# Windows: %LOCALAPPDATA%\Docker\log\### Common HTTP Status Codes with This Error
- 403 Forbidden: Usually indicates firewall blocking or geographic restrictions
- 404 Not Found: Registry URL typo or image doesn't exist
- 401 Unauthorized: Authentication required but credentials missing or invalid
- 407 Proxy Authentication Required: Corporate proxy needs credentials
### Registry API Testing
To fully test registry connectivity without Docker:
# Get auth token for Docker Hub
TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/nginx:pull" | jq -r .token)
# Test manifest fetch
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://registry-1.docker.io/v2/library/nginx/manifests/latest
# If this returns JSON, Docker should work
# If this returns HTML, something is intercepting the request### For Private Registry Operators
If you're operating a registry and users report this error:
1. Check reverse proxy configuration: Ensure error pages return JSON, not HTML
2. Configure proper error responses: The registry should return JSON even for errors
3. Review authentication flow: Auth failures should return JSON error responses
4. Check registry logs: Look for what's actually being returned to clients
Example nginx configuration for a registry:
location /v2/ {
proxy_pass http://registry:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Return JSON errors, not HTML
error_page 502 503 504 =503 @json_error;
}
location @json_error {
default_type application/json;
return 503 '{"errors":[{"code":"UNAVAILABLE","message":"Service temporarily unavailable"}]}';
}image operating system "linux" cannot be used on this platform
How to fix 'image operating system linux cannot be used on this platform' in Docker
manifest unknown: manifest unknown
How to fix 'manifest unknown' in Docker
cannot open '/etc/passwd': Permission denied
How to fix 'cannot open: Permission denied' in Docker
Error response from daemon: failed to create the ipvlan port
How to fix 'failed to create the ipvlan port' in Docker
toomanyrequests: Rate exceeded for anonymous users
How to fix 'Rate exceeded for anonymous users' in Docker Hub