Skip to content

Surface silent integration-assembly load failures (#16729)#17311

Merged
IEvangelist merged 1 commit into
mainfrom
dapine/harden-integration-resolver
May 20, 2026
Merged

Surface silent integration-assembly load failures (#16729)#17311
IEvangelist merged 1 commit into
mainfrom
dapine/harden-integration-resolver

Conversation

@IEvangelist
Copy link
Copy Markdown
Member

Description

When aspire new (or another integration-driven command) silently fails to find a code generator or language support — for any reason: build-pipeline version skew, a stale local NuGet cache, a transitive dependency mismatch, a partially populated probe directory, etc. — users see only this:

❌ An unexpected error occurred: No code generator found for language: TypeScript
❌ An unexpected error occurred: No language support found for: typescript/nodejs

The actual cause (e.g. a ReflectionTypeLoadException whose LoaderExceptions describe an Aspire.TypeSystem mismatch, or an integration assembly that was loaded but contributed zero discoverable types) is logged at LogDebug, which is below the file logger's default Information threshold and never reaches the on-disk log. Result: the user has no path to self-diagnose. Issue #16729 documents the symptom and the diagnostic-hardening direction the team agreed on after the originally-reported 13.3.0 reproduction turned out to be a local NuGet cache anomaly.

This is diagnostic hardening only — no change to runtime binding behavior. After this PR, a recurrence of the same shape of failure surfaces a clear log entry naming the offending assembly and either the LoaderExceptions text or the bundled-vs-libs version mismatch, and the user-facing error message itself lists the available languages or points at the apphost-server log + binary-mismatch scenario.

What users see after this PR

CLI log (file, default Information threshold):

warn: Aspire.Hosting.RemoteHost.AssemblyLoader[0]
      Shared assembly 'Aspire.TypeSystem' version mismatch: bundled=13.4.0.0, libs=13.3.0.0 (…/libs/Aspire.TypeSystem.dll).
      Integration assemblies referencing this assembly from the libs directory will fail to bind their type references through
      the default load context, which causes integrations to be silently skipped during type discovery.
      This typically indicates the apphost server bundle and the restored integration packages were produced by different build configurations.

warn: Aspire.Hosting.RemoteHost.CodeGeneration.CodeGeneratorResolver[0]
      Some types in assembly 'Aspire.Hosting.CodeGeneration.TypeScript' could not be loaded; 142 of 158 types are available.
      LoaderExceptions: Could not load file or assembly 'Aspire.TypeSystem, Version=13.3.0.0…'

warn: Aspire.Hosting.RemoteHost.CodeGeneration.CodeGeneratorResolver[0]
      Assembly 'Aspire.Hosting.CodeGeneration.TypeScript' was loaded but did not contribute any ICodeGenerator implementations.
      This is likely caused by a binary mismatch between the bundled and probed assemblies (see preceding LoaderExceptions).

User-facing error from the RPC service when there are some languages discovered but not the requested one:

No language support found for: klingon. Available languages: go, java, python, rust, typescript/nodejs.
No code generator found for language: klingon. Available languages: Go, Java, Python, Rust, TypeScript.

User-facing error when no languages are discovered at all (the silent-failure case the issue is about):

No language support found for: typescript/nodejs. No language support implementations were discovered in any loaded assembly.
This usually indicates a binary mismatch between the bundled apphost server and the integration assemblies on disk;
check the apphost server log for 'LoaderExceptions' Warnings.

Implementation summary

  • CodeGeneratorResolver / LanguageSupportResolver: promote ReflectionTypeLoadException from LogDebugLogWarning with flattened LoaderExceptions text. Add a per-assembly warning when an Aspire.Hosting.CodeGeneration.* assembly is loaded but contributes zero impls (almost always a silent type-load failure).
  • AssemblyLoader: add WarnIfSharedAssemblyMismatch, invoked at construction time. Compares the default-ALC version of each shared assembly (currently just Aspire.TypeSystem) against the libs-on-disk version and logs a Warning on mismatch. This catches the case the version-unification logic in IntegrationLoadContext.Load can't help with — for the shared assembly, the load context unconditionally defers to the default ALC, so a probed/bundled mismatch never goes through version comparison.
  • LanguageService / CodeGenerationService: replace bare "No language support found for: X" / "No code generator found for language: X" with a builder that lists available languages, or — when zero are discovered — explains the likely binary-mismatch cause and points at the apphost-server log.
  • PrebuiltAppHostServer: promote spawned-process stdout from LogTraceLogDebug and stderr from LogTraceLogInformation, so warnings emitted by the apphost server actually reach the default file log.

Internal-only API additions

  • CodeGeneratorResolver.GetSupportedLanguages() — used by CodeGenerationService to list discovered languages in error messages.
  • Internal test-only Func<IReadOnlyList<Assembly>> constructor on both resolvers, used by the new tests to inject synthetic assemblies without going through the file-system-probing AssemblyLoader.
  • IntegrationLoadContext.GetSharedAssemblyNames() — single source of truth for "which assemblies does the integration ALC defer to default for", consumed by the new WarnIfSharedAssemblyMismatch in AssemblyLoader.

All resolvers, services, and the load context are themselves internal sealed. No public API surface is added.

Validation

  • dotnet build src/Aspire.Hosting.RemoteHost/Aspire.Hosting.RemoteHost.csproj — 0 warnings, 0 errors.
  • dotnet build src/Aspire.Cli/Aspire.Cli.csproj — 0 warnings, 0 errors.
  • dotnet test --project tests/Aspire.Hosting.RemoteHost.Tests/Aspire.Hosting.RemoteHost.Tests.csproj -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"422 / 422 passed, including 8 new tests:
    • ResolverDiagnosticsTests (4) — Warning logged on ReflectionTypeLoadException with LoaderExceptions text (both resolvers); zero-contributor warning fires for marker-named assemblies and does not fire for arbitrary ones.
    • ServiceErrorMessageTests (4) — ScaffoldAppHost and GenerateCode error messages list discovered languages in the "some available" case and point at the binary-mismatch / LoaderExceptions hint in the "zero discovered" case.

Relationship to the closed PR

This supersedes the closed #16733. The diagnostic-hardening half is unchanged in intent and lands cleanly. The AssemblyLoader call site needed adjustment because LoadAssemblies no longer carries integrationLibsPath; the warning now runs from the constructor where libsPath is in scope. Test helpers updated for the current AssemblyLoader / LanguageService / CodeGenerationService constructor shapes (which all now also take RemoteHostProfilingTelemetry). The runtime binding fix the closed PR had originally explored is not included — the issue itself records that the originally-reported failure was a cache anomaly, not a binding regression.

Fixes #16729

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

When integration assembly discovery fails for any reason (build-pipeline
version skew, stale local NuGet cache, transitive dependency mismatch,
partially populated probe directory, etc.), the failure currently surfaces
only as:

  No code generator found for language: TypeScript
  No language support found for: typescript/nodejs

The underlying ReflectionTypeLoadException is swallowed at LogDebug, below
the file logger's default Information threshold, and never reaches the
CLI's on-disk log. This makes the user unable to self-diagnose.

This change hardens the diagnostic chain at every layer without altering
the success path:

* CodeGeneratorResolver / LanguageSupportResolver now log
  ReflectionTypeLoadException at Warning level and include the
  LoaderExceptions text in the message.
* When an assembly named Aspire.Hosting.CodeGeneration.* is loaded but
  contributes zero ICodeGenerator / ILanguageSupport types, log a Warning
  so the silent-failure case is visible.
* AssemblyLoader runs an Aspire.TypeSystem version sanity check at
  startup against the libs directory and warns when the bundled and
  probed versions diverge.
* LanguageService / CodeGenerationService error messages now list the
  available languages, or point at the apphost-server log + binary
  mismatch hint when zero have been discovered.
* PrebuiltAppHostServer promotes apphost-server stdout/stderr capture
  from Trace to Debug/Information, so warnings emitted by the apphost
  server reach the default file log.

Adds CodeGeneratorResolver.GetSupportedLanguages(), internal test-only
constructors on both resolvers that take a synthetic assembly factory,
and IntegrationLoadContext.GetSharedAssemblyNames(). Adds 8 new tests
covering the resolver warning paths and the user-facing error messages.

Closes #16729

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 20, 2026 13:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 diagnostics for integration-driven remote-host features (language support + code generation) so that silent integration assembly load/type discovery failures become visible in logs and produce actionable user-facing errors.

Changes:

  • Promote ReflectionTypeLoadException discovery failures to Warning with flattened LoaderExceptions, and warn when a codegen assembly loads but contributes zero implementations.
  • Enhance RPC service error messages to list discovered languages, or (when none are discovered) point users to binary-mismatch/LoaderExceptions warnings in the apphost-server log.
  • Add startup warning for shared-assembly version mismatches (Aspire.TypeSystem) and adjust CLI apphost-server stdout/stderr capture to reach default logs.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Aspire.Hosting.RemoteHost.Tests/ServiceErrorMessageTests.cs Adds coverage for improved user-facing error messages (but currently has constructor mismatch issues).
tests/Aspire.Hosting.RemoteHost.Tests/ResolverDiagnosticsTests.cs Adds coverage for warning logs on type-load failures and “zero contributor” assemblies.
tests/Aspire.Hosting.RemoteHost.Tests/RecordingLogger.cs Adds an in-memory logger helper for inspecting emitted log entries in tests.
src/Aspire.Hosting.RemoteHost/Language/LanguageSupportResolver.cs Adds a test seam for injected assemblies and upgrades logging for type-load/contribution failures.
src/Aspire.Hosting.RemoteHost/Language/LanguageService.cs Improves “no language support” errors with available-language listing / binary-mismatch guidance.
src/Aspire.Hosting.RemoteHost/IntegrationLoadContext.cs Adds shared-assembly-name helper for alignment with loader policy.
src/Aspire.Hosting.RemoteHost/CodeGeneration/CodeGeneratorResolver.cs Adds test seam + supported-languages enumeration + upgraded diagnostics.
src/Aspire.Hosting.RemoteHost/CodeGeneration/CodeGenerationService.cs Improves “no code generator” errors with available-language listing / binary-mismatch guidance.
src/Aspire.Hosting.RemoteHost/AssemblyLoader.cs Adds shared-assembly version mismatch warning at loader construction time.
src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Promotes apphost-server stdout/stderr logging levels so warnings reach default logs.

Comment thread tests/Aspire.Hosting.RemoteHost.Tests/ServiceErrorMessageTests.cs
Comment thread tests/Aspire.Hosting.RemoteHost.Tests/ServiceErrorMessageTests.cs
@github-actions
Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17311

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17311"

@IEvangelist
Copy link
Copy Markdown
Member Author

PR Testing Complete

Result: ✅ PR #17311 verified with caveats

Dogfooded PR CLI 13.4.0-pr.17311.g9874fb49, matching head 9874fb49.

Scenario Result Notes
Install + version verification ✅ PASS PR CLI matches head SHA
TypeScript scaffold ✅ PASS aspire new aspire-empty --language typescript succeeded; .modules\aspire.ts and apphost.ts generated
Unsupported language via aspire new --language klingon ⚠️ Not RemoteHost path CLI template guard rejects it first: Template 'aspire-empty' does not support language 'klingon'
Promoted apphost-server logging ✅ PASS CLI log contains [DBUG] [PrebuiltAppHostServer] ... stdout: lines from RemoteHost, verifying stdout promotion reaches file logs
Binary-mismatch / LoaderExceptions diagnostics ⚠️ Code-analysis + unit-test covered Skipped invasive hive mutation; PR includes focused resolver/service tests for this path

Key evidence from file logs:

[DBUG] [PrebuiltAppHostServer] PrebuiltAppHostServer(...) stdout: info: Aspire.Hosting.RemoteHost.JsonRpcServer[0]
[DBUG] [PrebuiltAppHostServer] PrebuiltAppHostServer(...) stdout:       Starting RemoteAppHost JsonRpc Server...

Cleanup: Temp workspace removed.

if (e.Data is not null)
{
_logger.LogTrace("PrebuiltAppHostServer({ProcessId}) stdout: {Line}", process.Id, e.Data);
// Promoted from LogTrace to LogDebug so that apphost-server stdout reaches the
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.

having history type comments like this are awkward IMO. "Promoted from LogTrace to LogDebug" won't really mean anything in a few weeks. We should word this so it explains why we are doing what we are doing.

@github-actions
Copy link
Copy Markdown
Contributor

CLI E2E Tests unknown — 94 passed, 0 failed, 2 unknown (commit 9874fb4)

View all recordings
Status Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View recording
AddPackageWhileAppHostRunningDetached ▶️ View recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View recording
AgentInitCommand_DefaultSelection_InstallsDefaultSkills ▶️ View recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View recording
AllPublishMethodsBuildDockerImages ▶️ View recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View recording
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost ▶️ View recording
AspireInitWithExistingAppHostDirRecreatesMissingNuGetConfigAndPreservesFiles ▶️ View recording
AspireInitWithSolutionFileGeneratesAppHostThatBuildsAgainstChannelHive ▶️ View recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View recording
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent ▶️ View recording
Banner_DisplayedOnFirstRun ▶️ View recording
Banner_DisplayedWithExplicitFlag ▶️ View recording
Banner_NotDisplayedWithNoLogoFlag ▶️ View recording
CertificatesClean_RemovesCertificates ▶️ View recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View recording
CreateAndRunAspireStarterProject ▶️ View recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View recording
CreateAndRunEmptyAppHostProject ▶️ View recording
CreateAndRunJavaEmptyAppHostProject ▶️ View recording
CreateAndRunJsReactProject ▶️ View recording
CreateAndRunPythonReactProject ▶️ View recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View recording
CreateAndRunTypeScriptStarterProject ▶️ View recording
CreateJavaAppHostWithViteApp ▶️ View recording
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain ▶️ View recording
DashboardRunWithOtelTracesReturnsNoTraces ▶️ View recording
DeployK8sBasicApiService ▶️ View recording
DeployK8sWithExternalHelmChart ▶️ View recording
DeployK8sWithGarnet ▶️ View recording
DeployK8sWithMongoDB ▶️ View recording
DeployK8sWithMySql ▶️ View recording
DeployK8sWithPostgres ▶️ View recording
DeployK8sWithRabbitMQ ▶️ View recording
DeployK8sWithRedis ▶️ View recording
DeployK8sWithSqlServer ▶️ View recording
DeployK8sWithValkey ▶️ View recording
DeployTypeScriptAppToKubernetes ▶️ View recording
DescribeCommandResolvesReplicaNames ▶️ View recording
DescribeCommandShowsRunningResources ▶️ View recording
DetachFormatJsonProducesValidJson ▶️ View recording
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance ▶️ View recording
DoListStepsShowsPipelineSteps ▶️ View recording
DocsCommand_RendersInteractiveMarkdownFromLocalSource ▶️ View recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View recording
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain ▶️ View recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View recording
GlobalMigration_PreservesAllValueTypes ▶️ View recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View recording
InitTypeScriptAppHost_AugmentsExistingViteRepoAtRoot ▶️ View recording
InteractiveCSharpInitCreatesExpectedFiles ▶️ View recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View recording
JavaScriptHostingApisRunFromTypeScriptAppHost ▶️ View recording
LatestCliCanStartStableChannelAppHost ▶️ View recording
LatestCliCanStartStableChannelTypeScriptAppHost ▶️ View recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View recording
LogLevelTrace_ProducesTraceEntriesInCliLogFile ▶️ View recording
LogsCommandShowsResourceLogs ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterApp ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterAppIsolated ▶️ View recording
PsCommandListsRunningAppHost ▶️ View recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View recording
PublishWithConfigureEnvFileUpdatesEnvOutput ▶️ View recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View recording
PublishWithoutOutputPathUsesAppHostDirectoryDefault ▶️ View recording
ResourceCommand_FailedExecution_DisplaysAppHostLogPathAndLogContainsEntries ▶️ View recording
ResourceCommand_FailsWhenInteractionServiceIsRequired ▶️ View recording
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput ▶️ View recording
RestoreGeneratesSdkFiles ▶️ View recording
RestoreGeneratesSdkFiles_WithConfiguredToolchain ▶️ View recording
RestoreRefreshesGeneratedSdkAfterAddingIntegration ▶️ View recording
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes ▶️ View recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View recording
RunPublishFailureScenarioAsync ▶️ View recording
RunReportsSyntaxErrorsForDotNetAppHost ▶️ View recording
RunReportsSyntaxErrorsForTypeScriptAppHost ▶️ View recording
SecretCrudOnDotNetAppHost ▶️ View recording
SecretCrudOnTypeScriptAppHost ▶️ View recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View recording
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets ▶️ View recording
StartReportsSyntaxErrorsForDotNetAppHost ▶️ View recording
StartReportsSyntaxErrorsForTypeScriptAppHost ▶️ View recording
StopAllAppHostsFromAppHostDirectory ▶️ View recording
StopJavaPolyglotAppHostUsingApphostDirectory ▶️ View recording
StopNonInteractiveSingleAppHost ▶️ View recording
StopTypeScriptPolyglotAppHostUsingApphostDirectory ▶️ View recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View recording
UnAwaitedChainsCompileWithAutoResolvePromises ▶️ View recording
UpdateProjectChannelToStable_TypeScript_PicksUpStablePackages ▶️ View recording

📹 Recordings uploaded automatically from CI run #26167426720

@IEvangelist IEvangelist merged commit fd03c7a into main May 20, 2026
602 of 605 checks passed
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.4 milestone May 20, 2026
aspire-repo-bot Bot added a commit to microsoft/aspire.dev that referenced this pull request May 20, 2026
…in aspire new

Documents the improved diagnostics surfaced in microsoft/aspire#17311:
- New error messages that list available languages when a specific language
  is not found
- The binary-mismatch error message and guidance when no language support
  implementations are discovered at all
- Resolution steps (aspire cache clear, checking the log for LoaderExceptions
  and version mismatch warnings)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@aspire-repo-bot
Copy link
Copy Markdown
Contributor

Pull request created: #1024

Generated by PR Documentation Check

@aspire-repo-bot
Copy link
Copy Markdown
Contributor

📝 Documentation has been drafted in microsoft/aspire.dev#1024 targeting release/13.4.

Added a Troubleshooting section to the aspire new command reference documenting the improved diagnostic error messages introduced by this PR (available-language listing and binary-mismatch guidance when no language support is discovered). The section also provides resolution steps and points users to the apphost-server log for LoaderExceptions warnings.

  • src/frontend/src/content/docs/reference/cli/commands/aspire-new.mdx — new Troubleshooting section (38 lines added)

Note

This draft PR needs human review before merging.

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.

Harden diagnostics when integration-assembly resolver discovers zero contributors

3 participants