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
- Validate the file with a YAML linter or `yamllint .github/workflows/` first.
- Read the wrapped error text — it contains the exact line/column from the YAML decoder.
- If the error mentions schema validation, see https://nektosact.com/usage/schema.html and fix or remove the non-schema keys.
- 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
- Lint every workflow change in CI before act sees it.
- Keep indentation consistent (2 spaces) in workflow YAML.
- Resolve merge conflicts in workflow files completely before running act.
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
- unable to read workflow '%s': file is empty: %w
- Actions YAML Schema Validation Error detected:\nFor more inf
- The runs.using key in action.yml must be one of: %v, got %s
- error occurring when resetting io pointer in '%s': %w
- workflow is not valid. '%s': Job name '%s' is invalid. Names
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/26349435dd4dcde2.
Report an issue: GitHub.