{"record":{"id":"7ae74fa815c4ec31","repo":"microsoft/aspire","slug":"resource-projectresource-name-does-not-carry-an","errorCode":null,"errorMessage":"Resource '{projectResource.Name}' does not carry an IProjectMetadata annotation.","messagePattern":"Resource '(.+?)' does not carry an IProjectMetadata annotation\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/ApplicationModel/ProjectResourceExtensions.cs","lineNumber":51,"sourceCode":"    /// <remarks>\n    /// A project resource must carry exactly one <see cref=\"IProjectMetadata\"/> annotation. Project metadata\n    /// cannot be replaced after project defaults have been applied because launch settings, endpoints, and\n    /// rebuild behavior are derived from that annotation.\n    /// </remarks>\n    /// <exception cref=\"InvalidOperationException\">Thrown when the project resource doesn't have exactly one project metadata annotation.</exception>\n    [AspireExportIgnore(Reason = \"Project metadata is a .NET-specific contract and is not part of the ATS surface.\")]\n    public static IProjectMetadata GetProjectMetadata(this ProjectResource projectResource)\n    {\n        ArgumentNullException.ThrowIfNull(projectResource);\n\n        return GetProjectMetadata((IResource)projectResource);\n    }\n\n    internal static IProjectMetadata GetProjectMetadata(this IResource projectResource)\n    {\n        if (!projectResource.TryGetProjectMetadata(out var projectMetadata))\n        {\n            throw new InvalidOperationException($\"Resource '{projectResource.Name}' does not carry an {nameof(IProjectMetadata)} annotation.\");\n        }\n\n        return projectMetadata;\n    }\n\n    internal static bool TryGetProjectMetadata(this IResource projectResource, [NotNullWhen(true)] out IProjectMetadata? projectMetadata)\n    {\n        ArgumentNullException.ThrowIfNull(projectResource);\n\n        projectMetadata = projectResource.Annotations.OfType<IProjectMetadata>().ToArray() switch\n        {\n            [] => null,\n            [var metadata] => metadata,\n            _ => throw new InvalidOperationException(\n                $\"Resource '{projectResource.Name}' carries more than one {nameof(IProjectMetadata)} annotation. \" +\n                \"Project resources must carry exactly one stable project metadata annotation.\")\n        };\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/ApplicationModel/ProjectResourceExtensions.cs#L33-L69","documentation":"GetProjectMetadata is the throwing variant of TryGetProjectMetadata: it requires the resource to carry exactly one IProjectMetadata annotation and throws InvalidOperationException naming the resource when none is present.","triggerScenarios":"Calling resource.GetProjectMetadata() on a non-project resource (container, executable, custom resource without project metadata).","commonSituations":"Generic code that assumes all resources in a collection are projects; calling project-only extension methods on a container resource; a builder misconfigured so the project annotation was never added.","solutions":["Use TryGetProjectMetadata and handle the false case instead of the throwing overload","Ensure the resource was created via AddProject (which adds IProjectMetadata) before calling GetProjectMetadata","Branch on resource type/annotations before invoking project-only extensions"],"exampleFix":"// before\nvar metadata = resource.GetProjectMetadata();\n// after\nif (!resource.TryGetProjectMetadata(out var metadata))\n{\n    return; // not a project resource\n}","handlingStrategy":"type-guard","validationCode":"if (!resource.TryGetProjectMetadata(out var metadata)) return; // or log and skip","typeGuard":"bool HasProjectMetadata([NotNullWhen(true)] IResource? r) => r is not null && r.TryGetProjectMetadata(out _);","tryCatchPattern":"try { var m = resource.GetProjectMetadata(); } catch (InvalidOperationException) { /* handle non-project resource */ }","preventionTips":["Prefer the Try pattern over the throwing Get overload in generic code","Create project resources only via AddProject"],"tags":["project-metadata","aspire-hosting"],"backgroundTag":"missing-dependency","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}