{"record":{"id":"83cd958bda222ee0","repo":"microsoft/aspire","slug":"resource-resourcegroup-key-name-binds-volumes-length","errorCode":null,"errorMessage":"Resource '{resourceGroup.Key.Name}' binds {volumes.Length} different Kubernetes persistent volumes named '{nameGroup.Key}' (from environments {environmentNames}). Run mode resolves both the local path and the container volume by name, so these would share one backing store. Publishing rejects this shape as well, because two resources cannot share the name '{nameGroup.Key}'. Give the volumes distinct names.","messagePattern":"Resource '(.+?)' binds (.+?) different Kubernetes persistent volumes named '(.+?)' \\(from environments (.+?)\\)\\. Run mode resolves both the local path and the container volume by name, so these would share one backing store\\. Publishing rejects this shape as well, because two resources cannot share the name '(.+?)'\\. Give the volumes distinct names\\.","errorType":"exception","errorClass":"DistributedApplicationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentExtensions.cs","lineNumber":151,"sourceCode":"        //\n        // Both run-mode paths key off the volume name alone — the local path lookup in\n        // VolumeMountBindingAnnotation.ResolvePath and the container mount rewrite in\n        // ApplyRunModeContainerVolumeName — so two distinct volumes sharing a name on one resource would\n        // silently resolve to a single backing store and mix their data. Reject that rather than teach\n        // both paths to disambiguate, because the shape can never be published: whichever local\n        // behavior we chose would only work in the inner loop.\n        foreach (var resourceGroup in bindings.GroupBy(item => item.Resource))\n        {\n            foreach (var nameGroup in resourceGroup.GroupBy(\n                item => item.Annotation.Volume.Name,\n                StringComparer.OrdinalIgnoreCase))\n            {\n                var volumes = nameGroup.Select(item => item.Annotation.Volume).Distinct().ToArray();\n\n                if (volumes.Length > 1)\n                {\n                    var environmentNames = string.Join(\", \", volumes.Select(volume => $\"'{volume.Parent.Name}'\"));\n                    throw new DistributedApplicationException(\n                        $\"Resource '{resourceGroup.Key.Name}' binds {volumes.Length} different Kubernetes persistent volumes named '{nameGroup.Key}' (from environments {environmentNames}). \" +\n                        $\"Run mode resolves both the local path and the container volume by name, so these would share one backing store. \" +\n                        $\"Publishing rejects this shape as well, because two resources cannot share the name '{nameGroup.Key}'. Give the volumes distinct names.\");\n                }\n            }\n        }\n    }\n\n    private static void ValidateRunModeBackingStoreCompatibility(PersistentVolumeBinding[] bindings)\n    {\n        // Host processes use an IAspireStore directory while containers use a named runtime\n        // volume. Treating those as one logical volume would silently split the data in run mode.\n        foreach (var environmentGroup in bindings.GroupBy(\n            item => item.Annotation.Volume.Parent.Name,\n            StringComparer.OrdinalIgnoreCase))\n        {\n            foreach (var volumeGroup in environmentGroup.GroupBy(\n                item => item.Annotation.Volume.Name,","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentExtensions.cs#L133-L169","documentation":"A single resource binds multiple distinct Kubernetes persistent volumes that share the same name (declared in different environments). Run mode resolves both the local path and container volume purely by name, so the two volumes would collide on one backing store; publish mode also rejects duplicate volume names. The validator throws DistributedApplicationException.","triggerScenarios":"A workload (via WithComputeEnvironment/WithPersistentVolume) binds two Kubernetes persistent volumes with identical names declared under two different Kubernetes environments, then running or publishing the AppHost.","commonSituations":"Multi-environment AppHosts (e.g. 'dev' and 'prod' Kubernetes environments) where volumes were copy-pasted with the same name and one workload references both.","solutions":["Give the two persistent volumes distinct names (e.g. 'data-dev' and 'data-prod').","Bind the workload to only one of the conflicting environments/volumes.","Consolidate the volume declarations into a single Kubernetes environment."],"exampleFix":"// before\nenv1.AddPersistentVolume(\"data\", ...);\nenv2.AddPersistentVolume(\"data\", ...); // duplicate name\n// after\nenv1.AddPersistentVolume(\"data-dev\", ...);\nenv2.AddPersistentVolume(\"data-prod\", ...);","handlingStrategy":"validation","validationCode":"var dupes = volumes.GroupBy(v => v.Name).Where(g => g.Count() > 1); if (dupes.Any()) throw new Exception($\"Duplicate volume names: {string.Join(',', dupes.Select(g => g.Key))}\");","typeGuard":null,"tryCatchPattern":"try { RunAppHostAsync(); } catch (DistributedApplicationException ex) when (ex.Message.Contains(\"different Kubernetes persistent volumes named\")) { /* rename conflicting volumes */ }","preventionTips":["Give volumes unique names across all environments","Avoid copy-pasting volume declarations across environments","Centralize volume naming with constants or a factory"],"tags":["kubernetes","volumes","naming-conflict","run-mode"],"backgroundTag":"conflicting-config-options","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}