Skip to content

Don't ignore substitution failures for BasicDerivation inputSrcs#520

Merged
edolstra merged 1 commit into
mainfrom
eelcodolstra/nix-437
Jun 23, 2026
Merged

Don't ignore substitution failures for BasicDerivation inputSrcs#520
edolstra merged 1 commit into
mainfrom
eelcodolstra/nix-437

Conversation

@edolstra

@edolstra edolstra commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Motivation

Previously, substitution failures for the inputSrcs of BasicDerivation were silently ignored, leading to inscrutable " 1 dependency failed" errors.

You now get:

error: path '/nix/store/ksv7jr3gl2damnbnn6rcigrzh8wql0rc-missing-dependency' is required, but there is no substituter that can build it
error: Cannot build '/nix/store/vis7l3rjqbb75kimqds2cqbm4wkf3qa2-test.drv'.
Reason: 1 dependency failed.
Output paths:
  /nix/store/y600378sad3qj81vn46g1lavwp7scgzl-test

Context

Summary by CodeRabbit

  • Bug Fixes
    • Corrected how missing substitution inputs/outputs are handled: paths marked as required now fail hard instead of falling back to “no substituters” behavior.
    • Updated substitution-goal creation and propagation so required paths consistently surface failures during derivation and store path ensuring/repair flows.
    • Refined exit-status behavior for substitution failures to better reflect “required” semantics across the build process.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a pathRequired boolean to PathSubstitutionGoal and Worker::makePathSubstitutionGoal. When true, the goal reports ecFailed instead of ecNoSubstituters if no substituters are available. The flag is propagated to child goals for referenced paths and call sites in ensurePath, repairPath, gaveUpOnSubstitution, haveDerivation, and repairClosure are updated accordingly.

Changes

pathRequired flag propagation

Layer / File(s) Summary
PathSubstitutionGoal and Worker header contracts
src/libstore/include/nix/store/build/substitution-goal.hh, src/libstore/include/nix/store/build/worker.hh
Adds bool pathRequired member to PathSubstitutionGoal and extends Worker::makePathSubstitutionGoal with the new parameter (default false).
PathSubstitutionGoal and Worker implementation
src/libstore/build/substitution-goal.cc, src/libstore/build/worker.cc
Constructor initializes pathRequired; child goals for referenced paths propagate the flag; init() now selects ecFailed when substituterFailed || pathRequired; Worker::makePathSubstitutionGoal and makeGoal forward the boolean to goal construction.
Call site updates
src/libstore/build/derivation-building-goal.cc, src/libstore/build/derivation-goal.cc, src/libstore/build/entry-points.cc
derivation-building-goal.cc passes true for missing inputs; derivation-goal.cc inserts explicit false for already-invalid output paths and repair scenarios; ensurePath and repairPath in entry-points.cc pass true to enforce hard failure when paths cannot be substituted.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

A boolean sprouts from the substitution tree,
pathRequired — set it and see!
When substituters vanish and paths can't be found,
ecFailed now echoes a much firmer sound.
The rabbit hops forward, no ambiguity near,
True means "must succeed," false means "no fear." 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: making substitution failures for BasicDerivation inputSrcs no longer be ignored, which aligns with the PR's core objective of improving error reporting.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eelcodolstra/nix-437

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.23.0)
src/libstore/build/derivation-building-goal.cc

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.43][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist

src/libstore/build/derivation-goal.cc

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.53][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist

src/libstore/build/entry-points.cc

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.47][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist

  • 4 others

Comment @coderabbitai help to get the list of available commands.

@edolstra edolstra force-pushed the eelcodolstra/nix-437 branch from ee24e7d to 8fa6857 Compare June 23, 2026 15:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/libstore/build/worker.cc`:
- Around line 109-112: The makePathSubstitutionGoal method caches goals in
substitutionGoals using only the path as the key, but ignores the
substitutionRequired parameter. When a cached goal is reused for the same path
with a different substitutionRequired value, the cached goal's original
requirement is preserved instead of honoring the new requirement. Fix this by
modifying the cache key to include the substitutionRequired parameter when
looking up or storing goals in substitutionGoals, or by validating that the
cached goal's substitutionRequired matches the new request and creating a new
goal if they differ.

In `@src/libstore/include/nix/store/build/worker.hh`:
- Around line 238-241: The function signature has a parameter order mismatch
where bool substitutionRequired precedes RepairFlag repair, causing three call
sites in worker.cc, entry-points.cc, and derivation-goal.cc to pass RepairFlag
values (like Repair or NoRepair) that implicitly convert to bool and bind to the
wrong parameter. Reorder the parameters in the function signature so that
RepairFlag repair comes before bool substitutionRequired, changing the signature
from (storePath, substitutionRequired, repair, ca) to (storePath, repair,
substitutionRequired, ca) or similar order that places the enum before the bool
to prevent implicit conversion and ensure the positional arguments at all three
call sites bind to the correct parameters.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f5401703-1577-49ca-a5c5-c86be20e8a49

📥 Commits

Reviewing files that changed from the base of the PR and between 32e4923 and ee24e7d.

📒 Files selected for processing (7)
  • src/libstore/build/derivation-building-goal.cc
  • src/libstore/build/derivation-goal.cc
  • src/libstore/build/entry-points.cc
  • src/libstore/build/substitution-goal.cc
  • src/libstore/build/worker.cc
  • src/libstore/include/nix/store/build/substitution-goal.hh
  • src/libstore/include/nix/store/build/worker.hh

Comment thread src/libstore/build/worker.cc Outdated
Comment thread src/libstore/include/nix/store/build/worker.hh
You now get:

  error: path '/nix/store/ksv7jr3gl2damnbnn6rcigrzh8wql0rc-missing-dependency' is required, but there is no substituter that can build it
  error: Cannot build '/nix/store/vis7l3rjqbb75kimqds2cqbm4wkf3qa2-test.drv'.
  Reason: 1 dependency failed.
  Output paths:
    /nix/store/y600378sad3qj81vn46g1lavwp7scgzl-test
@edolstra edolstra force-pushed the eelcodolstra/nix-437 branch from 8fa6857 to 8ca8648 Compare June 23, 2026 15:24

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/libstore/build/worker.cc (1)

109-112: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Cache reuse still drops pathRequired semantics for repeated requests.

On Line 112, substitutionGoals[path] reuses an existing goal regardless of the new pathRequired argument. That keeps behavior order-dependent: a prior non-required goal can cause later required callers to inherit ecNoSubstituters semantics (or the reverse).

Suggested fix
 std::shared_ptr<PathSubstitutionGoal> Worker::makePathSubstitutionGoal(
     const StorePath & path, bool pathRequired, RepairFlag repair, std::optional<ContentAddress> ca)
 {
-    return initGoalIfNeeded(substitutionGoals[path], path, *this, pathRequired, repair, ca);
+    auto & slot = substitutionGoals[path];
+    if (auto goal = slot.lock()) {
+        goal->pathRequired = goal->pathRequired || pathRequired;
+        return goal;
+    }
+    return initGoalIfNeeded(slot, path, *this, pathRequired, repair, ca);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/libstore/build/worker.cc` around lines 109 - 112, The
makePathSubstitutionGoal method reuses a cached goal from
substitutionGoals[path] without verifying that the pathRequired semantics match
the current request, causing order-dependent behavior. When retrieving an
existing goal via initGoalIfNeeded, check if the cached goal's pathRequired
value differs from the incoming pathRequired argument. If they differ, either
create a new goal instead of reusing the cached one or update the existing goal
to reflect the new pathRequired requirement. This ensures that repeated requests
with different pathRequired values are handled consistently rather than
inheriting stale semantics from the first caller.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/libstore/build/worker.cc`:
- Around line 109-112: The makePathSubstitutionGoal method reuses a cached goal
from substitutionGoals[path] without verifying that the pathRequired semantics
match the current request, causing order-dependent behavior. When retrieving an
existing goal via initGoalIfNeeded, check if the cached goal's pathRequired
value differs from the incoming pathRequired argument. If they differ, either
create a new goal instead of reusing the cached one or update the existing goal
to reflect the new pathRequired requirement. This ensures that repeated requests
with different pathRequired values are handled consistently rather than
inheriting stale semantics from the first caller.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 31313c12-0db0-4dcd-8fc5-55a5987b0a73

📥 Commits

Reviewing files that changed from the base of the PR and between 8fa6857 and 8ca8648.

📒 Files selected for processing (7)
  • src/libstore/build/derivation-building-goal.cc
  • src/libstore/build/derivation-goal.cc
  • src/libstore/build/entry-points.cc
  • src/libstore/build/substitution-goal.cc
  • src/libstore/build/worker.cc
  • src/libstore/include/nix/store/build/substitution-goal.hh
  • src/libstore/include/nix/store/build/worker.hh
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/libstore/build/derivation-building-goal.cc
  • src/libstore/include/nix/store/build/worker.hh
  • src/libstore/include/nix/store/build/substitution-goal.hh
  • src/libstore/build/substitution-goal.cc

@github-actions

Copy link
Copy Markdown

@github-actions github-actions Bot temporarily deployed to pull request June 23, 2026 15:31 Inactive
Comment thread src/libstore/build/substitution-goal.cc
StorePath storePath;

/**
* Whether, if there are not substituters, to return ecNoSubstituters or ecFailed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit:

Suggested change
* Whether, if there are not substituters, to return ecNoSubstituters or ecFailed.
* Whether, if there are no substituters that have this store path, to return ecNoSubstituters (which means......................) or ecFailed (which means.............).

follow-on from my previous comment (would be great to describe when ecNoSubs vs ecFailed will be returned and what that actually means/should mean...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is documented in PathSubstitutionGoal:

   /* Hack: don't indicate failure if there were no substituters.
       In that case the calling derivation should just do a
       build. */
    co_return doneFailure(
        substituterFailed || pathRequired ? ecFailed : ecNoSubstituters,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I definitely saw that comment, but somehow blanked it from my memory within 1 minute of reading it.... Thanks...

Then, nit:

Suggested change
* Whether, if there are not substituters, to return ecNoSubstituters or ecFailed.
* Whether, if there are no substituters, to return ecNoSubstituters or ecFailed.

@edolstra edolstra added this pull request to the merge queue Jun 23, 2026
Merged via the queue into main with commit 88aa0de Jun 23, 2026
33 checks passed
@edolstra edolstra deleted the eelcodolstra/nix-437 branch June 23, 2026 19:32
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