Skip to content

workflows/host: distinguish transient unavailable runner from missing runner#2259

Open
nadahalli wants to merge 3 commits into
mainfrom
tejaswi/tee-runner-graceful-subscribe
Open

workflows/host: distinguish transient unavailable runner from missing runner#2259
nadahalli wants to merge 3 commits into
mainfrom
tejaswi/tee-runner-graceful-subscribe

Conversation

@nadahalli

Copy link
Copy Markdown
Contributor

What

subscribe() in requirement_selecting_module returned a generic cannot find a runner error whenever no runner matched a trigger's requirements. That conflated two different cases:

  1. No runner of the required type is configured at all (permanent misconfiguration).
  2. A runner of the right type exists but cannot currently satisfy the requirement (transient), e.g. a capability that gates the runner is temporarily unavailable or not yet activated across the DON.

Case (2) now returns a typed ErrRunnerUnavailable, so callers can treat it as transient (hold + retry) rather than failing workflow initialization outright. Case (1) keeps the existing hard error.

Adds a generated HandlesRequirements helper (a runner-type check that mirrors CheckRequirements but ignores the value) to distinguish the two.

Why

On a mixed/rolling DON, the confidential-workflows capability (confidential-workflows@1.0.0-alpha) is not present on every node yet. The ConfidentialModule TEE runner exists on those nodes but reports zero regions, so subscribe failed hard for every confidential workflow, error-storming engine-init on nodes that do not yet have the capability. This lets the engine (chainlink core, follow-up PR) degrade gracefully and self-heal once the capability is DON-wide, instead of hard-failing.

Notes

  • Companion chainlink-core change (gate the ConfidentialModule requirement check in the subscribe path + treat ErrRunnerUnavailable as a transient hold) will consume this.
  • HandlesRequirements is generated via go generate ./pkg/workflows/host/ (template + regen, no hand-edited generated code).
  • Tests cover both cases (transient ErrRunnerUnavailable vs permanent hard error).

… runner

subscribe() previously returned a generic "cannot find a runner" error
whenever no runner matched a trigger's requirements, conflating two cases:
(1) no runner of the required type is configured at all (permanent
misconfiguration), and (2) a runner of the right type exists but cannot
currently satisfy the requirement, e.g. a capability that gates the runner is
temporarily unavailable or not yet activated across the DON (transient).

Case (2) now returns a typed ErrRunnerUnavailable so callers can hold and
retry rather than failing workflow initialization outright; case (1) keeps
the existing hard error. Adds a generated HandlesRequirements helper (a
runner-type check) to tell the two apart.

Lets the confidential-workflows engine degrade gracefully while the
confidential-workflows capability rolls out DON-wide, instead of
error-storming subscribe failures on nodes that don't yet have it.
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

✅ API Diff Results - github.com/smartcontractkit/chainlink-common

✅ Compatible Changes (2)

pkg/workflows/host (2)
  • ErrRunnerUnavailable — ➕ Added

  • HandlesRequirements — ➕ Added


📄 View full apidiff report

@nadahalli
nadahalli marked this pull request as ready for review July 16, 2026 14:06
@nadahalli
nadahalli requested a review from a team as a code owner July 16, 2026 14:06
Copilot AI review requested due to automatic review settings July 16, 2026 14:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves workflow runner selection error signaling by distinguishing between (a) no runner type being configured vs (b) an appropriate runner type existing but currently being unable to satisfy requirements (transient), enabling callers to retry transient initialization failures instead of failing permanently.

Changes:

  • Introduces ErrRunnerUnavailable to represent the “runner type exists but can’t currently satisfy requirements” case and returns it from subscribe() when appropriate.
  • Adds generated HandlesRequirements helper to detect whether a module covers a requirement type without evaluating requirement values.
  • Updates subscription-related tests to assert ErrRunnerUnavailable vs the existing hard error path.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
pkg/workflows/host/requirements_helper_gen.go Adds generated HandlesRequirements helper used to detect runner-type presence without enforcing requirements.
pkg/workflows/host/requirements_gen/requirements_helper.go.tmpl Updates generator template to emit HandlesRequirements for all sdk.Requirements fields.
pkg/workflows/host/requirement_selecting_module.go Returns ErrRunnerUnavailable when a compatible runner type exists but no runner can currently satisfy the requirements.
pkg/workflows/host/requirement_selecting_module_test.go Adjusts tests to validate transient vs permanent error behavior (ErrRunnerUnavailable vs generic failure).
Files not reviewed (1)
  • pkg/workflows/host/requirements_helper_gen.go: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +39 to +43
// HandlesRequirements reports whether handler covers every non-nil field in req (the
// right runner type), regardless of whether those callbacks pass. Distinguishes a
// present-but-unavailable runner from a missing one.
func HandlesRequirements(handler RequirementsHandler, req *sdk.Requirements) bool {
if req == nil {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants