microsoft/aspire · error · InvalidOperationException
Integration closure metadata line
Error message
Integration closure metadata line '{line}' is invalid. What it means
Each line of the closure metadata manifest must contain exactly 4 pipe-separated fields (artifact name, version, etc., parsed with Split('|', 4)). A line with fewer than 4 fields is structurally invalid — likely truncated or from an incompatible manifest format — so the parser throws InvalidOperationException quoting the offending line. Values are additionally normalized after the shape check.
Solutions
- Inspect the quoted line in the metadata manifest and restore the missing pipe-separated fields (expected shape: field1|field2|field3|field4).
- Regenerate the closure metadata by rebuilding the prebuilt AppHost instead of editing it by hand.
- Align CLI/bundle versions so the writer and parser use the same metadata line format.
- Escape or re-check how the writer serializes fields if package identifiers can contain '|'.
Example fix
// before (invalid, 3 fields) Newtonsoft.Json|13.0.3|sha256-abc123 // after (4 fields) Newtonsoft.Json|13.0.3|lib/net6.0/Newtonsoft.Json.dll|sha256-abc123
Defensive patterns
Strategy: validation
Validate before calling
static bool IsValidMetadataLine(string line) =>
!string.IsNullOrWhiteSpace(line) && line.Split('|', 4).Length == 4; Type guard
static bool TryParseClosureMetadata(string line, out string[] parts) { parts = line.Split('|', 4); return parts.Length == 4; } Try / catch
try { metadata = ParseClosureMetadata(line); }
catch (InvalidOperationException ex) when (ex.Message.Contains("metadata line"))
{ logger.LogError(ex, "Skipping corrupt metadata line — regenerate manifest"); } Prevention
- Never manually edit closure metadata files; rebuild instead.
- Remember the 4-field pipe format (field1|field2|field3|field4) when generating lines.
- Regenerate manifests after CLI version changes rather than diffing/patching them.
- Reject writers that emit partial lines (flush/verify file completeness).
When it happens
Trigger: Parsing a metadata manifest line that contains fewer than 4 '|'-delimited segments, e.g. a blank-ish line that escaped the empty-line filter, a manually edited line, or a line written by an older/newer format with 3 fields.
Common situations: Hand-editing the metadata file and dropping a field; a package name or path containing '|'-related corruption; version skew where the manifest was written by a different CLI version using a different field count; partial file write leaving a truncated last line.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Could not parse Helm version from 'helm version --short'…
- Downloaded Aspire skills package does not contain…
- Failed to parse template version from stdout.
- Integration closure manifest file
- Integration closure manifest is inconsistent. Sources
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/83dfbad2d5d4f6b4.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs:1570
private static async Task<List<string>> ReadManifestFileAsync(string filePath, CancellationToken cancellationToken)
{
if (!File.Exists(filePath))
{
throw new InvalidOperationException($"Integration closure manifest file '{filePath}' was not found after build.");
}
var lines = await File.ReadAllLinesAsync(filePath, cancellationToken).ConfigureAwait(false);
return lines.Where(static line => !string.IsNullOrWhiteSpace(line)).Select(static line => line.Trim()).ToList();
}
private static ClosureMetadata ParseClosureMetadata(string line)
{
ArgumentNullException.ThrowIfNull(line);
var parts = line.Split('|', 4);
if (parts.Length != 4)
{
throw new InvalidOperationException($"Integration closure metadata line '{line}' is invalid.");
}
return new ClosureMetadata(
NormalizeClosureMetadataValue(parts[0]),
NormalizeClosureMetadataValue(parts[1]),
NormalizeClosureMetadataValue(parts[2]),
NormalizeClosureMetadataValue(parts[3]));
}
private static string? NormalizeClosureMetadataValue(string value)
{
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
private static async Task<Dictionary<string, string>> ReadPackageFingerprintsAsync(string assetsFilePath, CancellationToken cancellationToken)
{
if (!File.Exists(assetsFilePath))
{View on GitHub (pinned to 25830f84bd)