{"record":{"id":"3f3ae2c627f25e51","repo":"microsoft/aspire","slug":"volume-source-and-target-must-be-set","errorCode":null,"errorMessage":"Volume source and target must be set","messagePattern":"Volume source and target must be set","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Docker/DockerComposeEnvironmentContext.cs","lineNumber":81,"sourceCode":"                    endpoint.IsExternal,\n                    endpoint.Name));\n        }\n    }\n\n    private void ProcessVolumes(DockerComposeServiceResource serviceResource)\n    {\n        if (!serviceResource.TargetResource.TryGetContainerMounts(out var mounts))\n        {\n            return;\n        }\n\n        var bindMountIndex = 0;\n\n        foreach (var mount in mounts)\n        {\n            if (mount.Source is null || mount.Target is null)\n            {\n                throw new InvalidOperationException(\"Volume source and target must be set\");\n            }\n\n            var source = mount.Source;\n            var name = mount.Source;\n\n            // For bind mounts, create environment placeholders for the source path\n            // Skip the docker socket which should be left as-is for portability\n            if (mount.Type == ContainerMountType.BindMount && !IsDockerSocket(mount.Source))\n            {\n                // Create environment variable name: {RESOURCE_NAME}_BINDMOUNT_{INDEX}\n                var envVarName = $\"{serviceResource.Name.ToUpperInvariant().Replace(\"-\", \"_\").Replace(\".\", \"_\")}_BINDMOUNT_{bindMountIndex}\";\n                bindMountIndex++;\n\n                // Add the placeholder to captured environment variables so it gets written to the .env file\n                // Use the original source path as the default value and pass the ContainerMountAnnotation as the source\n                var placeholder = environment.AddEnvironmentVariable(\n                    envVarName,\n                    description: $\"Bind mount source for {serviceResource.Name}:{mount.Target}\",","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Docker/DockerComposeEnvironmentContext.cs#L63-L99","documentation":"While converting the Aspire resource model to Docker Compose, ProcessVolumes iterates volume/bind-mount definitions and requires both Source and Target to be present. A mount missing either makes the compose volume entry undefined, so it throws InvalidOperationException. Docker Compose cannot express a named/bind volume without both sides.","triggerScenarios":"Publishing to a DockerComposeEnvironment when a resource has a volume/bind mount created with only one side set — e.g. builder.AddX(...).WithVolume(...) or WithBindMount where source path or container target path was passed as null.","commonSituations":"Anonymous-volume-style calls where developers passed null source expecting auto-generation; string interpolation producing empty/null paths; conditional code paths that skip setting the target.","solutions":["Provide both source and target in the WithVolume/WithBindMount call, e.g. .WithBindMount(\"./data\", \"/app/data\").","If an anonymous volume was intended, use the API variant designed for that instead of passing null source.","Inspect the resource whose mount is incomplete and fix the null argument at its definition site."],"exampleFix":"// before\n.WithBindMount(source: null, target: \"/app/data\")\n// after\n.WithBindMount(source: \"./data\", target: \"/app/data\");","handlingStrategy":"validation","validationCode":"// before publish, assert all mounts are complete\nforeach (var m in resource.Annotations.OfType<ContainerMountAnnotation>())\n    if (m.Source is null || m.Target is null)\n        throw new InvalidOperationException($\"Resource {resource.Name} has a mount missing source or target\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass both source and target to WithVolume/WithBindMount","Avoid conditional path expressions that can evaluate to null","Use the anonymous-volume API instead of null sources when auto-generation is wanted"],"tags":["docker-compose","volumes","bind-mount","publish"],"backgroundTag":"missing-required-argument","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"}