nektos/act · error
unable to build dependency graph for %s (%s)
Error message
unable to build dependency graph for %s (%s)
What it means
PlanJobs builds stages by repeatedly moving jobs whose dependencies are all satisfied into the next stage. If a pass adds zero runs but jobs remain, some job's `needs:` references either do not exist or form a cycle, and planning aborts. The message names the workflow (name and file) where the graph is unresolvable.
Source
Thrown at pkg/model/planner.go:375
jobIDs = newJobIDs
}
// next, build an execution graph
stages := make([]*Stage, 0)
for len(jobDependencies) > 0 {
stage := new(Stage)
for jID, jDeps := range jobDependencies {
// make sure all deps are in the graph already
if listInStages(jDeps, stages...) {
stage.Runs = append(stage.Runs, &Run{
Workflow: w,
JobID: jID,
})
delete(jobDependencies, jID)
}
}
if len(stage.Runs) == 0 {
return nil, fmt.Errorf("unable to build dependency graph for %s (%s)", w.Name, w.File)
}
stages = append(stages, stage)
}
return stages, nil
}
// return true iff all strings in srcList exist in at least one of the stages
func listInStages(srcList []string, stages ...*Stage) bool {
for _, src := range srcList {
found := false
for _, stage := range stages {
for _, search := range stage.GetJobIDs() {
if src == search {
found = true
}
}
}View on GitHub (pinned to 4f41128141)
Solutions
- Check the workflow file named in the message; verify every `needs:` entry matches an existing job ID exactly (case-sensitive).
- Break any dependency cycle by removing or reordering one needs edge.
- If a dependency is event-conditional, either make the dependent job conditional the same way or restructure so all scheduled jobs are plannable.
Example fix
# before
jobs:
build:
needs: deplooy
# after
jobs:
build:
needs: deploy Defensive patterns
Strategy: validation
Validate before calling
func validateNeeds(wf *Workflow) error {
ids := map[string]bool{}
for id := range wf.Jobs { ids[id] = true }
for id, j := range wf.Jobs {
for _, dep := range j.Needs {
if !ids[dep] { return fmt.Errorf("job %q needs unknown job %q", id, dep) }
}
}
// additionally run a DFS cycle check over the needs edges
return nil
} Try / catch
if err != nil && strings.Contains(err.Error(), "unable to build dependency graph") {
// the message names the workflow file; inspect its needs: edges for typos/cycles
} Prevention
- After renaming a job, grep the workflow for the old ID in needs: blocks.
- Keep dependency graphs acyclic by construction; draw them out for complex pipelines.
- Lint workflows with actionlint, which detects both unknown needs and cycles.
When it happens
Trigger: A job declares `needs: deplooy` (typo of deploy); a cycle like a→b→a; or a job depending on a job filtered out by the event/condition so it never enters a stage.
Common situations: Renaming a job without updating dependents' needs; copy-paste workflows introducing cyclic needs; references to jobs that only run on a different event.
Related errors
- unable to read workflow '%s': file is empty: %w
- workflow is not valid. '%s': %w
- error occurring when resetting io pointer in '%s': %w
- Could not find any stages to run. View the valid jobs with `
- 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/9449b3af3be4a1d6.
Report an issue: GitHub.