
The cause of aws ecr get-login-password | docker login resulting in 400 Bad Request in Windows PowerShell was BOM
This page has been translated by machine translation. View original
Introduction
Hello, I'm Kaieda.
I was trying to push a self-built container image to ECR from Windows PowerShell to run my personal "mechanism for extracting LLM activation values on GPU" on EKS.
However, when I piped the output of AWS CLI's aws ecr get-login-password to docker login, it failed with a 400 Bad Request.
Strangely, running the same command in Git Bash succeeds. When I traced the cause, I found that PowerShell's pipe was prepending a BOM (3 invisible bytes) to the beginning of the password.
This article summarizes the troubleshooting steps I took to find the cause, and how to fix it.
Conclusion
- In Windows PowerShell 5.1, when the terminal's code page is 65001 (UTF-8), passing a string via pipe to an external program prepends a UTF-8 BOM (
EF BB BF) to the beginning - The source of the BOM was the console character encoding
[Console]::InputEncoding docker login --password-stdinstrips trailing newlines but does not strip the leading BOM, so authentication to ECR fails with400 Bad Requestwhen the password includes a BOM- The workaround is one of the following:
- Wrap the command with
cmd /c "..." - Set
[Console]::InputEncodingto UTF-8 without BOM - Run it in bash, such as Git Bash
- Wrap the command with
Note that the same 400 Bad Request can also occur due to other causes. This article specifically covers the case occurring in Windows PowerShell 5.1 with code page 65001.
Environment
- Windows 11 Home
- Windows PowerShell 5.1.26100
- AWS CLI v2
- Docker Desktop for Windows (Docker CLI 29.8.2)
- Python (used to inspect byte sequences)
- Terminal code page: 65001 (UTF-8)
When you open PowerShell on a Japanese version of Windows, the code page is often 932. In that case, this problem did not occur (described later).
The reason it was 65001 this time was not because of settings in the PowerShell profile or registry, but because the terminal built into the application I was using was launching PowerShell with 65001 (no profiles had been created, and there were no code page settings in the registry or AutoRun settings for cmd).
What Happened
To log in to the ECR repository, I ran the following command (the account ID is an example).
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
The response was this.
Error response from daemon: login attempt to https://123456789012.dkr.ecr.us-east-1.amazonaws.com/v2/ failed with status: 400 Bad Request
Running aws ecr get-login-password alone returns the token correctly. The token generation itself seemed fine.
Troubleshooting the Cause
1. It Succeeds in Git Bash
First, I tried running the same command in Git Bash.
Login Succeeded
It succeeded easily. The AWS authentication and the token itself were correct, and it seemed to only fail when running in PowerShell.
2. Initially Suspected a Trailing Newline
A common issue heard about with PowerShell pipes is that "a newline is appended to the end of the passed string." When I looked into it, I found explanations that this error was also caused by a newline.
So at first, I thought a newline was the cause. However, I wanted to actually see what was being passed.
3. Inspecting What Is Being Passed Through the Pipe
I prepared a small Python script to display the received content as-is, to check how many bytes PowerShell's pipe was passing. First, I passed a string of 2,156 A characters instead of the token.
("A" * 2156) | python -c "import sys; d = sys.stdin.buffer.read(); print(len(d), d[:4], d[-4:])"
2161 b'\xef\xbb\xbfA' b'AA\r\n'
I passed 2,156 characters, but 2,161 bytes arrived. The breakdown of the 5 extra bytes is as follows:
\xef\xbb\xbfat the beginning (3 bytes)\r\nat the end (2 bytes)
The trailing newline was as expected, but something was also prepended. This EF BB BF is a BOM (Byte Order Mark). Originally used to indicate the byte order of characters, in UTF-8 it serves as a marker meaning "this string is written in UTF-8." However, when treated as part of a password, it becomes a completely different string.
I also verified with the actual token. I did not display the token content, only checking the length and the leading/trailing bytes.
aws ecr get-login-password --region us-east-1 | python -c "import sys; d = sys.stdin.buffer.read(); print(len(d), d[:3], d[-2:])"
2517 b'\xef\xbb\xbf' b'\r\n'
The token at that time was 2,512 characters (token length changes each time), and 2,517 bytes arrived. Even with the actual token, the leading 3 BOM bytes and the trailing 2 newline bytes were present.
※ If Microsoft Store opens when you type python, Python is not installed or is being intercepted by "App Execution Aliases." Please use py or the Python from a virtual environment.
4. Comparing with cmd's Pipe
I tried the same thing with cmd's pipe.
cmd /c "echo AAAA| python -c ""import sys; d = sys.stdin.buffer.read(); print(len(d), d[:4], d[-4:])"""
6 b'AAAA' b'AA\r\n'
With cmd, no BOM was prepended, only the trailing newline.
At this point, my initial assumption fell apart. docker login via cmd succeeds, yet cmd also appends a newline. This means the newline is not the cause.
Looking at the Docker CLI source code (readSecretFromStdin), the content read via --password-stdin is stripped of only the trailing newline, and everything else is used as-is. There is no processing to strip the BOM. The difference between PowerShell and cmd comes down to whether a leading BOM is present or not.
5. Comparing with Only the Code Page Changed
So why is the BOM being added? I checked the terminal's code page and found it was 65001 (UTF-8).
chcp
Current code page: 65001
To isolate the difference to just the code page, I launched a new PowerShell without loading the profile (-NoProfile) at both 65001 and 932 (the default for Japanese Windows) and ran the same check.
# 65001
cmd /c "chcp 65001 >nul & powershell -NoProfile -Command ""('A' * 10) | python -c 'import sys; d = sys.stdin.buffer.read(); print(len(d), d[:4], d[-4:])'"""
# 932
cmd /c "chcp 932 >nul & powershell -NoProfile -Command ""('A' * 10) | python -c 'import sys; d = sys.stdin.buffer.read(); print(len(d), d[:4], d[-4:])'"""
15 b'\xef\xbb\xbfA' b'AA\r\n'
12 b'AAAA' b'AA\r\n'
With 65001, the BOM was prepended; with 932, it was not.
Cause
Windows PowerShell 5.1 converts a string to bytes when passing it via pipe to an external program. The two settings that seem relevant here are $OutputEncoding and the console character encoding [Console]::InputEncoding. I inspected each in a new PowerShell with 65001.
cmd /c "chcp 65001 >nul & powershell -NoProfile -Command ""'OutputEncoding: ' + `$OutputEncoding.EncodingName + ' / BOM: ' + ((`$OutputEncoding.GetPreamble() | % { `$_.ToString('X2') }) -join ' '); 'Console.InputEncoding: ' + [Console]::InputEncoding.EncodingName + ' / BOM: ' + (([Console]::InputEncoding.GetPreamble() | % { `$_.ToString('X2') }) -join ' ')"""
OutputEncoding: US-ASCII / BOM:
Console.InputEncoding: Unicode (UTF-8) / BOM: EF BB BF
$OutputEncoding was US-ASCII as per the Windows PowerShell 5.1 default, with no BOM. On the other hand, [Console]::InputEncoding was UTF-8 with BOM when the code page was 65001.
I verified which one was the source of the BOM using actual docker login (all using a new PowerShell with -NoProfile).
| Code Page | Changed Setting | Result |
|---|---|---|
| 65001 | None | 400 Bad Request |
| 65001 | Only [Console]::InputEncoding set to UTF-8 without BOM |
Login Succeeded |
| 65001 | Only $OutputEncoding set to UTF-8 without BOM |
400 Bad Request |
| 932 | None | Login Succeeded |
It only succeeded when [Console]::InputEncoding was set to without BOM. The source of the BOM was [Console]::InputEncoding, and because docker login --password-stdin does not strip the BOM, the password with the BOM was being sent to ECR, resulting in 400 Bad Request.
Workarounds
Method 1: Wrap with cmd /c
This is the easiest approach. By delegating the pipe to cmd, no BOM is added.
cmd /c "aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com"
Login Succeeded
Method 2: Set [Console]::InputEncoding to UTF-8 without BOM
If you want to run it in PowerShell, set the console character encoding to UTF-8 without BOM.
[Console]::InputEncoding = New-Object System.Text.UTF8Encoding $false
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
Login Succeeded
This setting is effective until the terminal is closed.
Note that changing only $OutputEncoding did not fix the issue (see the table above). However, if you have set $OutputEncoding to UTF-8 with BOM ([System.Text.Encoding]::UTF8) in your profile or elsewhere, the BOM will also be added from there, so please set $OutputEncoding to without BOM as well.
Method 3: Run in bash
Running in bash, such as Git Bash, will not add a BOM. Git Bash succeeded from the start in this case as well.
Methods to Avoid
There is also a way to pass the password directly with -p, but the password will remain in the command history and process list. Docker itself warns you to use --password-stdin, so this is not recommended.
What I Did Not Try
This article only verified Windows PowerShell 5.1 with code pages 65001 and 932. The following were not tested:
- AWS Tools for PowerShell's
(Get-ECRLoginCommand).Password | docker login ...: This is the method introduced in AWS documentation for PowerShell. Since it also passes data to docker via PowerShell's pipe, the same issue may occur under the same conditions - PowerShell 7: Since the default for
$OutputEncodingis UTF-8 without BOM, the result may differ - Code pages other than 65001 and 932
Summary
- In a Windows PowerShell 5.1 environment with code page 65001 (UTF-8),
[Console]::InputEncodingbecomes UTF-8 with BOM, and a BOM is prepended to strings passed through pipes - Since
docker login --password-stdindoes not strip the BOM, it results in400 Bad Request - The issue can be resolved by wrapping with
cmd /c, setting[Console]::InputEncodingto UTF-8 without BOM, or running in bash
At first I thought "it's probably just a newline issue," but when I actually inspected the byte sequence, the culprit was hiding as 3 bytes at the very beginning. I'm glad I checked it myself.
I hope this helps anyone who is struggling with the same error.
References
- docker/cli: cli/command/registry/login.go(readSecretFromStdin)
- about_Preference_Variables($OutputEncoding)- PowerShell 5.1 | Microsoft Learn
- about_Character_Encoding - PowerShell 7.5 | Microsoft Learn
- Private registry authentication - Amazon ECR User Guide
- Using AWS's ECR get-login-password and --password-stdin with Powershell - Docker Community Forums
