nektos/act · error

workflow is not valid. '%s': %w

Error message

workflow is not valid. '%s': %w

What it means

Generic wrapper returned when ReadWorkflow fails with anything other than io.EOF while parsing a workflow file from .github/workflows. The %w chain carries the real cause: YAML syntax errors, strict-mode schema violations, or unsupported node types. This is the planner path (directory scan); the file name is included.

Source

Thrown at pkg/model/planner.go:133

	}

	wp := new(workflowPlanner)
	for _, wf := range workflows {
		ext := filepath.Ext(wf.workflowDirEntry.Name())
		if ext == ".yml" || ext == ".yaml" {
			f, err := os.Open(filepath.Join(wf.dirPath, wf.workflowDirEntry.Name()))
			if err != nil {
				return nil, err
			}

			log.Debugf("Reading workflow '%s'", f.Name())
			workflow, err := ReadWorkflow(f, strict)
			if err != nil {
				_ = f.Close()
				if err == io.EOF {
					return nil, fmt.Errorf("unable to read workflow '%s': file is empty: %w", wf.workflowDirEntry.Name(), err)
				}
				return nil, fmt.Errorf("workflow is not valid. '%s': %w", wf.workflowDirEntry.Name(), err)
			}
			_, err = f.Seek(0, 0)
			if err != nil {
				_ = f.Close()
				return nil, fmt.Errorf("error occurring when resetting io pointer in '%s': %w", wf.workflowDirEntry.Name(), err)
			}

			workflow.File = wf.workflowDirEntry.Name()
			if workflow.Name == "" {
				workflow.Name = wf.workflowDirEntry.Name()
			}

			err = validateJobName(workflow)
			if err != nil {
				_ = f.Close()
				return nil, err
			}

View on GitHub (pinned to 4f41128141)

Solutions

  1. Validate the file with a YAML linter or `yamllint .github/workflows/` first.
  2. Read the wrapped error text — it contains the exact line/column from the YAML decoder.
  3. If the error mentions schema validation, see https://nektosact.com/usage/schema.html and fix or remove the non-schema keys.
  4. Re-run `act` with the same event to confirm the fixed file plans cleanly.

Example fix

# before
jobs:
  build:
   runs-on: ubuntu-latest   # wrong indent
# after
jobs:
  build:
    runs-on: ubuntu-latest
Defensive patterns

Strategy: try-catch

Validate before calling

cmd := exec.Command("yamllint", "-d", "relaxed", ".github/workflows/")
if err := cmd.Run(); err != nil { return fmt.Errorf("workflows failed lint: %w", err) }

Try / catch

plan, err := model.NewWorkflowPlanner(dir, strict)
if err != nil {
    var joined interface{ Unwrap() []error }
    if errors.As(err, &joined) {
        for _, e := range joined.Unwrap() { log.Error(e) } // schema details
    }
    return err
}

Prevention

When it happens

Trigger: A workflow file with invalid YAML indentation, duplicate keys, tabs, or a structure failing schema validation in strict mode. Any parse error except plain EOF lands here.

Common situations: Hand-edited workflows with broken indentation; anchors/aliases used in ways resolveAliases cannot handle; running act with strict schema validation on workflows using undocumented keys; merge conflicts left unresolved in workflow files.

Related errors


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