nektos/act · error
failed to handle credentials: %s
Error message
failed to handle credentials: %s
What it means
startJobContainer resolves registry credentials for the job's container image via rc.handleCredentials, which interpolates the username/password expressions from workflow 'container.credentials' and (for GitHub-hosted images) can use GITHUB_TOKEN / CR_PAT style secrets. Failure is wrapped with '%s' (non-wrapping format) so the original cause text is appended verbatim.
Source
Thrown at pkg/runner/run_context.go:271
}
func (rc *RunContext) startJobContainer() common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
image := rc.platformImage(ctx)
rawLogger := logger.WithField("raw_output", true)
logWriter := common.NewLineWriter(rc.commandHandler(ctx), func(s string) bool {
if rc.Config.LogOutput {
rawLogger.Infof("%s", s)
} else {
rawLogger.Debugf("%s", s)
}
return true
})
username, password, err := rc.handleCredentials(ctx)
if err != nil {
return fmt.Errorf("failed to handle credentials: %s", err)
}
logger.Infof("\U0001f680 Start image=%s", image)
name := rc.jobContainerName()
envList := make([]string, 0)
envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TOOL_CACHE", "/opt/hostedtoolcache"))
envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_OS", "Linux"))
envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_ARCH", container.RunnerArch(ctx)))
envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TEMP", "/tmp"))
envList = append(envList, fmt.Sprintf("%s=%s", "LANG", "C.UTF-8")) // Use same locale as GitHub Actions
ext := container.LinuxContainerEnvironmentExtensions{}
binds, mounts := rc.GetBindsAndMounts()
// specify the network to which the container will connect when `docker create` stage. (like execute command line: docker create --network <networkName> <image>)
// if using service containers, will create a new network for the containers.View on GitHub (pinned to 4f41128141)
Solutions
- Pass the required secrets: act -s CR_USERNAME=user -s CR_PASSWORD=pass and reference them in container.credentials.
- Verify the credential expressions in the workflow's 'container.credentials' interpolate to non-empty values.
- Docker-login once on the host (docker login ghcr.io) so the image can be pulled without workflow-level credentials.
- Check the appended cause text in the message — it names exactly which credential failed.
Example fix
# before (workflow)
container:
image: ghcr.io/org/img:1
credentials:
username: ${{ secrets.CR_USER }}
password: ${{ secrets.CR_PASS }}
# run without secrets
# after
act -s CR_USER=user -s CR_PASS=token -j build Defensive patterns
Strategy: validation
Validate before calling
# fail fast if required credential secrets are absent for jobs with container images
for s in CR_USERNAME CR_PASSWORD; do
[ -n "${!s}" ] || { echo "missing secret $s"; exit 1; }
done
docker login ghcr.io -u "$CR_USERNAME" -p "$CR_PASSWORD" >/dev/null && echo registry-auth-ok Prevention
- Always invoke act with -s for every secret the workflow's container.credentials reference.
- docker login on the host as a fallback so pulls need no workflow credentials.
- Read the appended cause text — it names the failing credential.
When it happens
Trigger: Job 'container: image: ...' block with 'credentials: username/password' whose expressions fail to evaluate, are empty, or reference missing secrets; handleCredentials returns an error when the image requires auth but no usable credentials resolve.
Common situations: Private registry (ghcr.io, ECR, Artifactory) image without passing secrets: act -s USERNAME=... -s PASSWORD=...; expression typo in credentials block; expired token.
Related errors
- failed to handle service %s credentials: %w
- %s is not a valid mac address
- invalid value: %d. Valid memory swappiness range is 0-100
- invalid range format for --expose: %w
- network %q is specified multiple times
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/21aee239bac6e1d5.
Report an issue: GitHub.