nektos/act · error
Invalid run/uses syntax for job:%s step:%+v
Error message
Invalid run/uses syntax for job:%s step:%+v
What it means
The step factory could not classify the step: model.Step.Type() returned StepTypeInvalid, which happens when both `run:` and `uses:` are empty, or when both are set on the same step. The factory therefore has no step implementation to instantiate and returns this error naming the job and echoing the whole step model.
Source
Thrown at pkg/runner/step_factory.go:18
package runner
import (
"fmt"
"github.com/nektos/act/pkg/model"
)
type stepFactory interface {
newStep(step *model.Step, rc *RunContext) (step, error)
}
type stepFactoryImpl struct{}
func (sf *stepFactoryImpl) newStep(stepModel *model.Step, rc *RunContext) (step, error) {
switch stepModel.Type() {
case model.StepTypeInvalid:
return nil, fmt.Errorf("Invalid run/uses syntax for job:%s step:%+v", rc.Run, stepModel)
case model.StepTypeRun:
return &stepRun{
Step: stepModel,
RunContext: rc,
}, nil
case model.StepTypeUsesActionLocal:
return &stepActionLocal{
Step: stepModel,
RunContext: rc,
readAction: readActionImpl,
runAction: runActionImpl,
}, nil
case model.StepTypeUsesActionRemote:
return &stepActionRemote{
Step: stepModel,
RunContext: rc,
readAction: readActionImpl,
runAction: runActionImpl,View on GitHub (pinned to 4f41128141)
Solutions
- Give each step exactly one of `run:` or `uses:`.
- Check indentation: keys of a step must align under the same '- ' item; a misplaced run: one level up yields an empty step.
- Run actionlint on the workflow — it flags steps missing run/uses immediately.
Example fix
# before - name: build run: make build uses: actions/setup-go@v5 # both set # after - name: build uses: actions/setup-go@v5 - name: build run: make build
Defensive patterns
Strategy: validation
Validate before calling
python3 - <<'EOF'
import yaml,sys
for f in __import__('glob').glob('.github/workflows/*.yml'):
wf=yaml.safe_load(open(f))
for j in wf.get('jobs',{}).values():
for s in j.get('steps',[]):
has_run=bool(s.get('run')); has_uses=bool(s.get('uses'))
if has_run == has_uses: # both or neither
sys.exit(f"{f}: step {s.get('name')} must have exactly one of run/uses")
EOF Type guard
func stepWellFormed(s map[string]any) bool {
_, r := s["run"]; _, u := s["uses"]
return r != u // exactly one present
} Prevention
- Exactly one of run or uses per step — never both, never neither.
- Watch YAML indentation under '- name:'.
- Run actionlint in CI; it detects structurally invalid steps.
When it happens
Trigger: A step block in the workflow has neither `run:` nor `uses:`, or declares both at once; often a side effect of YAML indentation putting keys at the wrong level so the step struct ends up empty.
Common situations: Copy-paste merging two step examples leaving both keys; YAML indentation that detaches run/uses from the step map (making them job-level or ignored); a step reduced to only `name:`/`with:` after editing; template placeholders never filled in.
Related errors
- ErrShortRef
- The runs.using key in action.yml must be one of: %v, got %s
- workflow is not valid. '%s': %w
- Actions YAML Schema Validation Error detected:\nFor more inf
- invalid Step %v: missing run or uses key
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/d47560f04599bffc.
Report an issue: GitHub.