#7154·woodpecker

depends_on gate can report success after a dependency failure: Task.DepStatus is never persisted (v3.18.1)

Author: finedesignzCreated Sep 15, 2026Updated Sep 15, 2026

Task.DepStatus updates from server/queue/fifo.go::updateDepStatusInQueue are never written back to the persistent task store (no store.TaskUpdate exists -- only TaskInsert/TaskDelete), so a workflow whose dependency already finished before a queue reload (server restart, HA leader change) sees an empty DepStatus and Task.ShouldRun() vacuously returns true regardless of the dependency's actual result.

Repro

Pipeline 5543, workflow 39994 (a depends_on aggregate "gate" workflow) depending on workflow 39997 (a required dependency workflow), which reached terminal failure at unix time 1789479910. Workflow 39994 started at 1789480893 (~983s / ~16 min later) and reported success.

Root cause (traced against a shallow clone of v3.18.1)

  • pipeline/frontend/builder/builder.go computes item.RunsOn via IncludesStatusSuccess/IncludesStatusFailure.
  • pipeline/frontend/yaml/constraint/constraint.go's Constraint.Match never reads workflow status, so an explicit when: status: [success] on the dependent workflow does not change gating behavior (proven no-op).
  • server/model/task.go:70 Task.ShouldRun() loops for _, status := range t.DepStatus -- an empty map ranges zero times and the function returns true.
  • server/queue/fifo.go::updateDepStatusInQueue updates DepStatus only on the in-memory queue.
  • server/store/store.go exposes only TaskInsert (called once at enqueue time with an empty DepStatus) and TaskDelete (called once on finish/poll) for tasks -- there is no TaskUpdate.
  • server/queue/persistent.go::WithTaskStore reloads the queue from the store on startup/leader change, which restores tasks with their stale, empty DepStatus from insert time if they are still queued.

Impact

A depends_on aggregate gate workflow (a common pattern for exposing one required branch-protection status context while fanning out to several path-filtered or parallel workflows) can report success even though a required dependency already failed, if the server restarts or an HA leader change happens while the gate workflow is still queued. This defeats branch-protection gating silently -- there is no error, just a wrong success.

Environment

  • woodpecker-server v3.18.1
  • Confirmed via GET /api/repos/{id}/pipelines/{number} timestamps on the reproduction above, not merely inferred from logs.

Suggested fix direction

Persist DepStatus writes (a TaskUpdate on the store, called from updateDepStatusInQueue or equivalent) so a queue reload restores the real dependency state instead of an empty map, or make Task.ShouldRun() on an empty DepStatus re-derive it from already-terminal sibling workflows in the same pipeline instead of assuming success.

Source: woodpecker-ci/woodpecker