microsoft/aspire · error · InvalidOperationException
Integration closure manifest is inconsistent. Sources
Error message
Integration closure manifest is inconsistent. Sources: {sourcePaths.Count}, metadata: {metadataLines.Count}, targets: {targetPaths.Count}. What it means
PrebuiltAppHostServer reads three parallel manifest files (sources, metadata lines, targets) describing the integration closure after a bundled restore/build. All three must have the same line count because line N of each file describes the same artifact. If the counts differ, the manifest was written inconsistently and the closure cannot be reconstructed safely, so the CLI throws InvalidOperationException.
Solutions
- Delete the bundle/restore output directory and rebuild the prebuilt AppHost so all three manifests are regenerated together.
- Regenerate the manifest with the same Aspire CLI version that consumes it; do not mix versions.
- If hand-maintained, verify each file has one entry per artifact in identical order (sources[i], metadata[i], targets[i] describe the same artifact).
Example fix
// before: manifest edited, sources has 5 lines but targets has 4 // after: aspire publish / dotnet build # regenerate the closure so Sources: 5, metadata: 5, targets: 5
Defensive patterns
Strategy: validation
Validate before calling
var src = File.ReadAllLines(closureSourcesPath).Where(l => !string.IsNullOrWhiteSpace(l)).Count();
var meta = File.ReadAllLines(closureMetadataPath).Where(l => !string.IsNullOrWhiteSpace(l)).Count();
var tgt = File.ReadAllLines(closureTargetsPath).Where(l => !string.IsNullOrWhiteSpace(l)).Count();
if (src != meta || src != tgt) throw new InvalidOperationException($"Closure manifests out of sync: sources={src}, metadata={meta}, targets={tgt}; rebuild the bundle."); Type guard
static bool ClosureManifestsConsistent(int sources, int metadata, int targets) => sources == metadata && sources == targets;
Try / catch
try { await server.LoadClosureAsync(ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("closure manifest is inconsistent"))
{ logger.LogWarning(ex, "Corrupt closure manifest; regenerating bundle"); await RegenerateBundleAsync(ct); } Prevention
- Never hand-edit closure manifest files; always regenerate via build.
- Keep all three manifest files together when copying bundle output.
- Use the same CLI version to write and read bundles.
- Add a build step that asserts the three manifest line counts match before packaging.
When it happens
Trigger: Loading a prebuilt AppHost bundle whose closure manifest files (sources, metadata, targets) were produced with mismatched line counts — e.g. a partially written, hand-edited, or stale manifest from a different build, or a manifest where blank lines were filtered differently at write time.
Common situations: Copying only some manifest files from another build output; an interrupted build that wrote one manifest completely but not others; manually editing one manifest (adding/removing a path) without updating the other two; using a bundle generated by an older CLI version with a different manifest format.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Integration closure manifest file
- Downloaded Aspire skills package does not contain…
- Integration assets file
- Integration closure metadata line
- Manifest creation failed
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/9d43ad5f36a9138d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs:827
_logger.LogError("Integration project build failed. Output:\n{BuildOutput}", outputLines);
throw new AppHostServerPrepareFailedException(GetIntegrationBuildFailureMessage(buildOutput), buildOutput);
}
if (restoreFingerprint is not null && !skipRestore)
{
await WriteRestoreStampAsync(restoreDir, restoreFingerprint, _logger, cancellationToken).ConfigureAwait(false);
}
var closureSourcesPath = Path.Combine(restoreDir, ClosureSourcesFileName);
var closureMetadataPath = Path.Combine(restoreDir, ClosureMetadataFileName);
var closureTargetsPath = Path.Combine(restoreDir, ClosureTargetsFileName);
var sourcePaths = await ReadManifestFileAsync(closureSourcesPath, cancellationToken).ConfigureAwait(false);
var metadataLines = await ReadManifestFileAsync(closureMetadataPath, cancellationToken).ConfigureAwait(false);
var targetPaths = await ReadManifestFileAsync(closureTargetsPath, cancellationToken).ConfigureAwait(false);
if (sourcePaths.Count != metadataLines.Count || sourcePaths.Count != targetPaths.Count)
{
throw new InvalidOperationException(
$"Integration closure manifest is inconsistent. Sources: {sourcePaths.Count}, metadata: {metadataLines.Count}, targets: {targetPaths.Count}.");
}
var projectRefAssemblyNames = await ReadProjectRefAssemblyNamesAsync(
Path.Combine(restoreDir, ProjectRefAssemblyNamesFileName),
cancellationToken).ConfigureAwait(false);
var appSettingsContent = CreateAppSettingsContent(packageRefs, projectRefAssemblyNames);
var packageFingerprints = await ReadPackageFingerprintsAsync(
Path.Combine(restoreDir, "obj", ProjectAssetsFileName),
cancellationToken).ConfigureAwait(false);
var closureEntries = new List<AppHostServerClosureSource>(sourcePaths.Count);
for (var i = 0; i < sourcePaths.Count; i++)
{
var metadata = ParseClosureMetadata(metadataLines[i]);
var packageSha512 = TryGetPackageFingerprint(packageFingerprints, metadata);
closureEntries.Add(new AppHostServerClosureSource(View on GitHub (pinned to 25830f84bd)