nektos/act · error

invalid Step %v: missing run or uses key

Error message

invalid Step %v: missing run or uses key

What it means

In the job executor's step-building loop (pkg/runner/job_executor.go), act iterates infoSteps produced by splitting each step's 'run'/'uses' interpolation. If a step model comes back nil, the step had neither a 'run:' nor a 'uses:' key after parsing — the executor factory inserted a nil placeholder — and this error names the offending step index.

Source

Thrown at pkg/runner/job_executor.go:69

			rc.Env[k] = rc.ExprEval.Interpolate(ctx, v)
		}
		return nil
	})

	var setJobError = func(ctx context.Context, err error) error {
		if err == nil {
			return nil
		}
		logger := common.Logger(ctx)
		logger.Errorf("%v", err)
		common.SetJobError(ctx, err)
		return err
	}

	for i, stepModel := range infoSteps {
		if stepModel == nil {
			return func(_ context.Context) error {
				return fmt.Errorf("invalid Step %v: missing run or uses key", i)
			}
		}
		if stepModel.ID == "" {
			stepModel.ID = fmt.Sprintf("%d", i)
		}

		step, err := sf.newStep(stepModel, rc)

		if err != nil {
			return common.NewErrorExecutor(err)
		}

		preSteps = append(preSteps, useStepLogger(rc, stepModel, stepStagePre, step.pre().ThenError(setJobError)))

		stepExec := step.main()
		steps = append(steps, useStepLogger(rc, stepModel, stepStageMain, func(ctx context.Context) error {
			err := stepExec(ctx)
			if err != nil {

View on GitHub (pinned to 4f41128141)

Solutions

  1. Go to the job's steps list; the failing step is at the printed index (0-based). Add a valid 'run:' or 'uses:' to it, or delete the step.
  2. Run yamllint/actionlint on the workflow to catch structure errors.
  3. Check indentation: 'run'/'uses' must be direct children of the step map item.

Example fix

# before
steps:
  - name: build
    if: always()

# after
steps:
  - name: build
    if: always()
    run: make build
Defensive patterns

Strategy: validation

Validate before calling

python3 - <<'EOF'
import yaml,glob
for f in glob.glob('.github/workflows/*.y*ml'):
    y=yaml.safe_load(open(f))
    for jid,job in (y.get('jobs') or {}).items():
        for i,s in enumerate(job.get('steps') or []):
            if s is None or not ('run' in s or 'uses' in s):
                raise SystemExit(f'{f}: job {jid} step index {i} has no run/uses key')
print('steps ok')
EOF

Prevention

When it happens

Trigger: A workflow job step that has neither 'run' nor 'uses' (e.g. only 'name:' and 'if:'), or a step whose keys are misindented so the parser sees an empty step entry. The index i is the 0-based position in the job's steps list.

Common situations: Copy-paste where the 'run:' line was deleted; YAML indentation making 'run' a child of another key; placeholder/commented-out step bodies.

Related errors


AI-assisted analysis of nektos/act@4f41128141 (2026-08-15). Data as JSON: /api/errors/7a59504d0ed47e48. Report an issue: GitHub.