nektos/act · error
failed to handle service %s credentials: %w
Error message
failed to handle service %s credentials: %w
What it means
In the same startJobContainer flow, for each entry under the job's 'services:' map act resolves that service's registry credentials via handleServiceCredentials(spec.Credentials). Any error is wrapped with the service ID and %w, so the cause (missing secret, bad expression, invalid credentials config) chains through.
Source
Thrown at pkg/runner/run_context.go:306
// 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.
// and it will be removed after at last.
networkName, createAndDeleteNetwork := rc.networkName()
// add service containers
for serviceID, spec := range rc.Run.Job().Services {
// interpolate env
interpolatedEnvs := make(map[string]string, len(spec.Env))
for k, v := range spec.Env {
interpolatedEnvs[k] = rc.ExprEval.Interpolate(ctx, v)
}
envs := make([]string, 0, len(interpolatedEnvs))
for k, v := range interpolatedEnvs {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}
username, password, err = rc.handleServiceCredentials(ctx, spec.Credentials)
if err != nil {
return fmt.Errorf("failed to handle service %s credentials: %w", serviceID, err)
}
interpolatedVolumes := make([]string, 0, len(spec.Volumes))
for _, volume := range spec.Volumes {
interpolatedVolumes = append(interpolatedVolumes, rc.ExprEval.Interpolate(ctx, volume))
}
serviceBinds, serviceMounts := rc.GetServiceBindsAndMounts(interpolatedVolumes)
interpolatedPorts := make([]string, 0, len(spec.Ports))
for _, port := range spec.Ports {
interpolatedPorts = append(interpolatedPorts, rc.ExprEval.Interpolate(ctx, port))
}
exposedPorts, portBindings, err := nat.ParsePortSpecs(interpolatedPorts)
if err != nil {
return fmt.Errorf("failed to parse service %s ports: %w", serviceID, err)
}
imageName := rc.ExprEval.Interpolate(ctx, spec.Image)View on GitHub (pinned to 4f41128141)
Solutions
- Identify the service from the error message and supply its secrets: act -s SERVICE_USERNAME=... -s SERVICE_PASSWORD=... matching the expressions in the workflow.
- docker login to the registry on the host so pulls succeed without per-service credentials, then remove the credentials block if appropriate.
- Verify the service's credentials expressions interpolate (act --verbose shows the underlying cause via %w).
- Rotate/verify the token if the registry returns 401.
Example fix
# before (workflow)
services:
redis:
image: registry.internal/redis:7
credentials:
username: ${{ secrets.REG_USER }}
password: ${{ secrets.REG_PASS }}
# run without secrets
# after
act -s REG_USER=me -s REG_PASS=token -j test Defensive patterns
Strategy: validation
Validate before calling
# list every secret referenced by service credentials and verify presence
python3 - <<'EOF'
import re,glob,yaml
refs=set()
for f in glob.glob('.github/workflows/*.y*ml'):
for job in (yaml.safe_load(open(f)).get('jobs') or {}).values():
for svc in (job.get('services') or {}).values():
c=svc.get('credentials') or {}
for v in c.values(): refs |= set(re.findall(r'secrets\.([A-Za-z_][\w]*)', str(v)))
print('required secrets:', sorted(refs))
EOF
# then pass each: act -s NAME=value Prevention
- Map every secrets.* reference in services.<id>.credentials to an act -s flag.
- Pre-authenticate the host with docker login for private registries.
- The error names serviceID — fix that service's credentials first.
When it happens
Trigger: A services.<id> block with 'credentials: username/password' referencing secrets not provided to act, or a credentials mapping that fails interpolation; the error names which service (serviceID) failed.
Common situations: Service containers from private registries (postgres from Artifactory, ghcr.io images) without -s secret flags; empty username/password after expression evaluation; expired registry token.
Related errors
- failed to handle credentials: %s
- %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/efcccfaef2d69643.
Report an issue: GitHub.