Problem
On machines without traditional Docker installed, any operation that requires registry credentials hard-fails because config.Load() requires ~/.docker/config.json to exist on disk.
Reproduction:
ERROR: failed to create sandbox: create sandbox: create sandbox: run sandbox:
apply definition hook: failed to retrieve registry credentials for
docker/sandbox-templates:shell: load config: config path:
config file does not exist (C:\Users\docker\.docker\config.json)
This machine has no ~/.docker/ directory and no config.json.
Root Cause
The error chain is a cascade of hard failures with no graceful fallback:
1. config/load.go — hard-fails on missing files
Dir() (line ~61): returns error if ~/.docker directory doesn't exist
Filepath() (line ~85): returns error if config.json doesn't exist
Load() (line ~106): calls Filepath(), propagates the error as "config path: %w"
2. config/auth.go — no fallback to credential helpers
AuthConfigs() (line ~18): calls Load(), wraps error as "load config: %w", returns immediately
AuthConfigForHostname() (line ~31): same behavior — contradicts its own docstring which claims it "will attempt to load registry credentials using the default credential helper for the platform"
3. Image pull propagates the failure
image/options.go:59 — WithCredentialsFromConfig() calls AuthConfigs(), propagates error
image/pull.go:70 — wraps as "set credentials for pull option: %w"
container/lifecycle.go:321 — defaultPullHook calls image.Pull() → hard-fail
What Should Happen
When config.json is missing, the auth functions should:
- Not fail — a missing config file is a valid state (fresh install, CI environments, alternative runtimes)
- Fall through to credential helpers —
config/credentials_helpers.go already implements platform-specific helpers (wincred, osxkeychain, pass, secretservice) but they're never reached because Load() fails first
- Return empty credentials as a last resort — public images don't need auth
Existing graceful pattern in this repo
context/current.go:25-26 already handles missing config correctly:
if os.IsNotExist(err) {
return DefaultContextName, nil // Graceful fallback
}
This pattern should be replicated in the auth functions.
Proposed Solution
The docker/secrets-engine project handles credential retrieval robustly without requiring a Docker config file on disk. The secrets-engine team should be well-positioned to contribute a fix here that:
- Makes
config.Load() / auth functions tolerate a missing config file
- Integrates with secrets-engine for credential resolution when config.json is absent
- Falls back to platform credential helpers (already implemented in
credentials_helpers.go but unreachable)
- Returns empty credentials as a final fallback for public registries
Files That Need Changes
| File |
Function |
Issue |
config/load.go |
Dir(), Filepath() |
Hard-fails if ~/.docker or config.json missing |
config/auth.go |
AuthConfigs() |
Doesn't fallback to credential helpers on missing config |
config/auth.go |
AuthConfigForHostname() |
Same — contradicts its own docstring |
image/options.go |
WithCredentialsFromConfig() |
No fallback to empty credentials |
cc @docker/secrets-engine team
Problem
On machines without traditional Docker installed, any operation that requires registry credentials hard-fails because
config.Load()requires~/.docker/config.jsonto exist on disk.Reproduction:
This machine has no
~/.docker/directory and noconfig.json.Root Cause
The error chain is a cascade of hard failures with no graceful fallback:
1.
config/load.go— hard-fails on missing filesDir()(line ~61): returns error if~/.dockerdirectory doesn't existFilepath()(line ~85): returns error ifconfig.jsondoesn't existLoad()(line ~106): callsFilepath(), propagates the error as"config path: %w"2.
config/auth.go— no fallback to credential helpersAuthConfigs()(line ~18): callsLoad(), wraps error as"load config: %w", returns immediatelyAuthConfigForHostname()(line ~31): same behavior — contradicts its own docstring which claims it "will attempt to load registry credentials using the default credential helper for the platform"3. Image pull propagates the failure
image/options.go:59—WithCredentialsFromConfig()callsAuthConfigs(), propagates errorimage/pull.go:70— wraps as"set credentials for pull option: %w"container/lifecycle.go:321—defaultPullHookcallsimage.Pull()→ hard-failWhat Should Happen
When
config.jsonis missing, the auth functions should:config/credentials_helpers.goalready implements platform-specific helpers (wincred, osxkeychain, pass, secretservice) but they're never reached becauseLoad()fails firstExisting graceful pattern in this repo
context/current.go:25-26already handles missing config correctly:This pattern should be replicated in the auth functions.
Proposed Solution
The docker/secrets-engine project handles credential retrieval robustly without requiring a Docker config file on disk. The secrets-engine team should be well-positioned to contribute a fix here that:
config.Load()/ auth functions tolerate a missing config filecredentials_helpers.gobut unreachable)Files That Need Changes
config/load.goDir(),Filepath()~/.dockerorconfig.jsonmissingconfig/auth.goAuthConfigs()config/auth.goAuthConfigForHostname()image/options.goWithCredentialsFromConfig()cc @docker/secrets-engine team