{"record":{"id":"9ec2251c146b0cc6","repo":"microsoft/aspire","slug":"both-sourcepath-and-contents-are-set-for-a-file-entry","errorCode":null,"errorMessage":"Both SourcePath and Contents are set for a file entry","messagePattern":"Both SourcePath and Contents are set for a file entry","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Dcp/Model/Container.cs","lineNumber":405,"sourceCode":"\n        var entry = new ContainerFileSystemEntry\n        {\n            Type = type,\n            Name = item.Name,\n            Owner = item.Owner,\n            Group = item.Group,\n            Mode = (int)item.Mode,\n        };\n\n        if (item is ContainerFileBase file)\n        {\n            entry.Source = file.SourcePath;\n            entry.Contents = file.Contents;\n            entry.ContinueOnError = file.ContinueOnError;\n\n            if (file.Contents is not null && file.SourcePath is not null)\n            {\n                throw new ArgumentException(\"Both SourcePath and Contents are set for a file entry\");\n            }\n        }\n        else if (item is ContainerDirectory directory)\n        {\n            entry.Entries = directory.Entries?.Select(e => e.ToContainerFileSystemEntry()).ToList();\n        }\n\n        return entry;\n    }\n}\n\ninternal sealed class ContainerFileSystemEntry : IEquatable<ContainerFileSystemEntry>\n{\n    // The type of the file system entry (file or directory)\n    [JsonPropertyName(\"type\")]\n    public string Type { get; set; } = ContainerFileSystemEntryType.File;\n\n    // The name of the file system entry","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Dcp/Model/Container.cs#L387-L423","documentation":"Thrown when a ContainerFile entry specifies both a SourcePath (a file to copy from the host) and inline Contents. These are mutually exclusive ways to supply the file body, so the converter rejects the entry with an ArgumentException during DCP model serialization.","triggerScenarios":"Constructing a ContainerFile (or using the corresponding resource builder API) with both Contents set to a non-null string and SourcePath set to a non-null path, then adding it to a container's file annotations.","commonSituations":"Copy-pasted configuration where a template sets Contents but a caller also fills in SourcePath; merging two configuration sources that each supplied one of the fields.","solutions":["Remove one of the two: keep SourcePath to copy from disk, or keep Contents for inline content.","If both behaviors are needed, create two separate file entries (one sourced, one inline) at different destinations.","Centralize file-entry construction in a helper that asserts only one is set before building the ContainerFile."],"exampleFix":"// before\nnew ContainerFile { SourcePath = \"./conf/app.conf\", Contents = \"key=value\", Destination = \"/etc/app.conf\" }\n// after\nnew ContainerFile { SourcePath = \"./conf/app.conf\", Destination = \"/etc/app.conf\" }","handlingStrategy":"validation","validationCode":"if (file.SourcePath is not null && file.Contents is not null)\n    throw new ArgumentException(\"Set either SourcePath or Contents, not both.\");","typeGuard":null,"tryCatchPattern":"try { AddFileEntry(file); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"SourcePath\")) { /* fix config: pick one source */ }","preventionTips":["Use distinct builder methods for sourced vs inline files (CopyFile vs SetContents).","Validate container file entries at model-construction time, not serialization time.","When merging configs, prefer explicit overwrite semantics over field-wise merge."],"tags":["containers","configuration","mutually-exclusive-options"],"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-21T04:17:39.646Z"}