Skip to content

aspire restore skips re-restore when an older CLI replaced the integration assets, silently reusing the old code generator #19603

Open

Description

@flojon

aspire restore silently runs the previous CLI version's TypeScript code generator after a version switch

Summary

aspire restore rewrites the generated IntegrationRestore.csproj with the current CLI's package version, but does not re-restore or rebuild it. The previously built generator assembly stays in bin/, so code generation runs with the old CLI's generator while reporting success. Any codegen fix in a newer CLI silently does not apply until the user deletes .aspire/ by hand.

Repro

A TypeScript AppHost. Two CLI versions installed — released 13.5.1 and a dogfood build 13.6.0-pr.19577.gfa0aea2c (which contains the #19577 codegen fix).

# 1. clean generate with the newer CLI  -> correct output
rm -rf .aspire && aspire-13.6.0-pr restore
# 2. generate with the older CLI        -> old (buggy) output, as expected
aspire-13.5.1 restore
# 3. generate with the newer CLI again  -> STILL the old buggy output
aspire-13.6.0-pr restore

Step 3 prints ✅ SDK code restored successfully with no warning.

Observed state after step 3

What Value
integration-restore/IntegrationRestore.csproj PackageReference 13.6.0-pr.19577.gfa0aea2c ✅ rewritten
RestoreAdditionalProjectSources points at the new hive ✅
integration-restore/obj/project.assets.json Aspire.Hosting.CodeGeneration.TypeScript/13.5.1 ❌
integration-restore/bin/Debug/net10.0/Aspire.Hosting.CodeGeneration.TypeScript.dll 13.5.1+69db530a… ❌
generated .aspire/modules/aspire.mts old generator's output ❌
exit / message success, no warning ❌

File mtimes show the ordering:

09:57:36  IntegrationRestore.csproj                 <- rewritten to 13.6.0-pr
09:57:19  obj/project.assets.json                   <- 17s EARLIER, still 13.5.1
07:54:51  obj/aspire-restore.stamp                  <- hours old, never refreshed
2026-08-20 19:03  bin/.../CodeGeneration.TypeScript.dll   <- 13.5.1 package content

obj/aspire-restore.stamp never being refreshed looks like the gate: the CLI decides the integration restore is up to date and skips it, despite having just written a csproj that pins a different package version. .codegen-hash does change, so code generation itself re-runs — it just runs the stale generator.

The new package is present and resolvable (~/.aspire/hives/pr-19577/packages/Aspire.Hosting.CodeGeneration.TypeScript.13.6.0-pr.19577.gfa0aea2c.nupkg), so this is not a resolution failure.

Asymmetry

Going down (13.6.0-pr → 13.5.1) does rebuild correctly — csproj, project.assets.json, and bin/ all move to 13.5.1. Only the second switch fails to rebuild, which is what makes it easy to miss.

Why it matters

The failure mode is invisible and actively misleading. Verifying a codegen fix by installing a PR build and re-running aspire restore reports success and produces the unfixed output — which reads as "the fix does not work." It cost me a false negative while validating #19577 against the sample from #19507.

Deleting .aspire/modules is not sufficient — codegen re-runs against the stale generator in .aspire/integrations/. Only rm -rf .aspire works.

Suggestions

  • Include the CLI / codegen package version in whatever obj/aspire-restore.stamp gates on, so a version change forces re-restore and rebuild.
  • Or compare the resolved version in project.assets.json against the version just written to the csproj, and re-restore on mismatch.
  • At minimum, warn when the built generator assembly's version differs from the running CLI's, instead of reporting plain success.

Environment

  • Aspire CLI 13.5.1+69db530a4816698cf1d5fa4557933e0ac4f127c6 and 13.6.0-pr.19577.gfa0aea2c
  • linux-x64 (WSL2), .NET SDK with net10.0 target
  • Dogfood build installed via eng/scripts/get-aspire-cli-pr.sh 19577

Related

Activity

  1. flojon commented on Aug 22, 2026

    @flojon
    ContributorAuthor

    Correcting the scope of my own report — the title generalizes too far. Follow-up testing with a third CLI (released 13.5.2) narrows it considerably.

    I could not reproduce this between two released versions. It only reproduces when returning to a PR/dogfood build whose package comes from a local hive folder source.

    Each run below is aspire restore on the same TypeScript AppHost, recording the pinned version in the generated IntegrationRestore.csproj, the version NuGet actually resolved in obj/project.assets.json, and the version of the generator assembly in bin/:

    Sequence csproj pin assets.json built bin/ dll Consistent?
    clean → 13.5.1 13.5.1 13.5.1 13.5.1 ✅
    → 13.5.2 13.5.2 13.5.2 13.5.2 ✅
    → back to 13.5.1 13.5.1 13.5.1 13.5.1 ✅
    clean → 13.5.1 13.5.1 13.5.1 13.5.1 ✅
    → 13.5.2 13.5.2 13.5.2 13.5.2 ✅
    → 13.6.0-pr (first time) 13.6.0-pr 13.6.0-pr 13.6.0-pr ✅
    → 13.5.2 13.5.2 13.5.2 13.5.2 ✅
    → back to 13.6.0-pr 13.6.0-pr 13.5.2 13.5.2 ❌

    Minimal deterministic repro, confirmed twice:

    rm -rf .aspire
    aspire-13.6.0-pr restore     # correct
    aspire-13.5.2   restore      # correct
    aspire-13.6.0-pr restore     # csproj updated, restore and build skipped
    

    So two conditions both appear necessary: the target version must be a hive-sourced PR build, and it must have been built in that directory before. A first-time switch to the dogfood build works correctly, which is why the plain A → B case never showed it.

    The mtimes in the failing run point at ordering rather than a stamp:

    10:07:47  IntegrationRestore.csproj      <- rewritten to 13.6.0-pr
    10:07:37  obj/project.assets.json        <- 10s EARLIER, resolved 13.5.2
    10:07:37  obj/project.nuget.cache        <- same
    10:07:27  obj/aspire-restore.stamp       <- older still, never refreshed
    

    Restore ran against the csproj as it stood before the rewrite, and nothing re-restored afterwards. RestoreAdditionalProjectSources does end up correct in the final csproj (the hive path is present), so the hive is reachable — it just wasn't in the csproj that restore actually consumed.

    Practical impact is therefore narrower than I first wrote, but lands squarely on PR validation: install a dogfood build, compare against a release, go back to the dogfood build, and you silently get the release's generator while restore reports success. That is exactly the loop someone runs to verify a codegen fix. Everything else in the report stands — deleting .aspire/modules is still insufficient, and rm -rf .aspire is still the workaround.

    Suggest retitling to something like "aspire restore skips re-restore when switching back to a previously-built PR/hive package version, silently reusing the old code generator."

  2. joperezr commented on Aug 22, 2026

    @joperezr
    Member
    🤖 Aspire triage pass — 2026-08-22 08:54 UTC

    Area: area-cli, area-ats
    Kind: bug
    Repro: yes-minimal — see the ## Repro section (3-step version-switch sequence)
    Affected version(s): 13.5.1, 13.6.0-pr.19577 (dogfood PR build)
    Severity: S3 — only triggers on a newer→older→newer CLI version switch; workaround rm -rf .aspire exists. Impact is a misleading silent success during codegen-fix validation.

    Urgency: ⚪ backlog — no milestone
    Reason: build-staleness bug in aspire restore integration-restore gating, not a confirmed regression, and a workaround exists. Default to backlog while 13.5 stabilizes; a human can pull it up if it's blocking codegen-fix validation more broadly.

    Suggested owner(s): Sébastien Ros (@sebastienros) (score 11), backup Mitch Denny (@mitchdenny) (score 6)
    Evidence:

    Confidence: medium
    Needs human?: no

  3. changed the title [-]aspire restore silently runs the previous CLI version's TypeScript code generator after a version switch[/-] [+]aspire restore skips re-restore when switching back to a previously-built PR/hive package version, silently reusing the old code generator[/+] on Aug 22, 2026
  4. changed the title [-]aspire restore skips re-restore when switching back to a previously-built PR/hive package version, silently reusing the old code generator[/-] [+]aspire restore skips re-restore when an older CLI replaced the integration assets, silently reusing the old code generator[/+] on Aug 22, 2026
  5. flojon commented on Aug 22, 2026

    @flojon
    ContributorAuthor

    Correcting my own narrowing above — the PR/hive condition is confounded, and the
    scope is wider than that comment concluded.

    While verifying #19604 against this repro I checked what the intervening CLI
    actually does. 13.5.1 has no skip-restore logic at all. Its debug line is:

    PrebuiltAppHostServer: Building integration project with 2 packages and 1 project references
    

    where the 13.6.0-pr.19577 build logs the same line with (restore requested)
    appended. Across three separate 13.5.1 restores in the same directory the stamp
    was never created, read, or refreshed — it stayed at the dogfood run's value and
    mtime throughout. 13.5.1 simply always restores.

    So the two necessary conditions are about which CLI has the feature, not about
    where its packages come from:

    • the version you return to must be stamp-aware — it writes a stamp and then
      trusts it on the next launch
    • the intervening version must be stamp-unaware — it restores, replaces
      project.assets.json, and never touches the stamp, so the earlier stamp survives
      intact and the returning CLI reproduces its own earlier fingerprint

    The reason I couldn't reproduce it between two released versions is that neither
    13.5.1 nor 13.5.2 has the stamp logic yet — not that they were released rather
    than hive-sourced. The only stamp-aware build I had was a PR one, so "hive-sourced"
    tracked "has the feature" perfectly and I read the wrong condition out of it.

    That matters for severity. Once skip-restore ships in a release, this reproduces
    between two plain released versions — newer-with-stamp → older-without-stamp →
    newer-with-stamp — with no dogfood build involved. It's a latent general bug rather
    than a PR-validation-only one, which I think undercuts the "only triggers on a
    newer→older→newer CLI version switch, workaround exists" read in the triage pass.

    Retitled accordingly. #19604 fixes it by binding the stamp to a fingerprint of the
    assets it produced, which is version-agnostic and so covers the general case too,
    not just the hive one I originally described.

  6. flojon commented on Oct 25, 2026

    @flojon
    ContributorAuthor

    Update: #19763 removed the aspire-restore.stamp skip logic. On current main, PrebuiltAppHostServer always restores (noRestore: false), so the version-switch sequence in this issue should no longer leave stale assets. I verified this by reading the code, not by re-running the repro on a main build. Builds that still contain the stamp logic (e.g. the 13.6.0-pr dogfood build) can still reproduce it. I've closed the fix PR #19604 as obsolete. Leaving this issue open for a maintainer to confirm and close.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions