{"record":{"id":"e14d3395e03c1fcb","repo":"hashicorp/terraform","slug":"source-and-content-cannot-both-be-null","errorCode":null,"errorMessage":"source and content cannot both be null","messagePattern":"source and content cannot both be null","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/builtin/provisioners/file/resource_provisioner.go","lineNumber":156,"sourceCode":"\t\tfile, err := os.CreateTemp(\"\", \"tf-file-content\")\n\t\tif err != nil {\n\t\t\treturn \"\", true, err\n\t\t}\n\n\t\tif _, err = file.WriteString(content.AsString()); err != nil {\n\t\t\tfile.Close()\n\t\t\treturn \"\", true, err\n\t\t}\n\n\t\tfile.Close()\n\t\treturn file.Name(), true, nil\n\n\tcase !src.IsNull():\n\t\texpansion, err := homedir.Expand(src.AsString())\n\t\treturn expansion, false, err\n\n\tdefault:\n\t\treturn \"\", false, errors.New(\"source and content cannot both be null\")\n\t}\n}\n\n// copyFiles is used to copy the files from a source to a destination\nfunc copyFiles(ctx context.Context, comm communicator.Communicator, src, dst string) error {\n\tretryCtx, cancel := context.WithTimeout(ctx, comm.Timeout())\n\tdefer cancel()\n\n\t// Wait and retry until we establish the connection\n\terr := communicator.Retry(retryCtx, func() error {\n\t\treturn comm.Connect(nil)\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// disconnect when the context is canceled, which will close this after\n\t// Apply as well.","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/builtin/provisioners/file/resource_provisioner.go#L138-L174","documentation":"Defensive runtime error inside the file provisioner's source/content expansion helper (resource_provisioner.go:156, the `default` arm). After validating at config time, the provisioner expands the source path (homedir.Expand) or returns the content blob; if execution reaches the default branch both inputs were null at runtime, which should be unreachable given ValidateProvisionerConfig. Seeing it means validation was bypassed or the coerced config differs at runtime.","triggerScenarios":"ProvisionResource is invoked with a config where both `source` and `content` evaluate to null at runtime (e.g. via dynamic/conditional values), skipping the validation-time guard and reaching the default branch of the switch.","commonSituations":"Content/source coming from a variable, local, or data source that evaluates to null under certain conditions. A third-party harness calling the provisioner gRPC plugin directly without running ValidateProvisionerConfig.","solutions":["Ensure the provisioner block has exactly one of source/content resolving to a concrete value at apply time.","Run terraform validate before apply so the validation-time check catches the missing attribute first.","If a variable supplies the value, give it a non-null default or guard the provisioner with a count/for_each condition."],"exampleFix":"# before\nvariable \"app_conf\" { type = string }\nprovisioner \"file\" {\n  content     = var.app_conf   # null when unset\n  destination = \"/etc/app/app.conf\"\n}\n# after\nvariable \"app_conf\" { type = string }\nprovisioner \"file\" {\n  source      = \"./app.conf\"   # concrete value\n  destination = \"/etc/app/app.conf\"\n}","handlingStrategy":"validation","validationCode":"// Ensure at least one payload source is non-null at runtime before calling ProvisionResource.\nif source.IsNull() && content.IsNull() {\n    return errors.New(\"source and content cannot both be null\")\n}","typeGuard":"func hasRuntimePayload(src, content cty.Value) bool {\n    return !src.IsNull() || !content.IsNull()\n}","tryCatchPattern":null,"preventionTips":["Run terraform validate so the validation-time guard fires before apply.","Guard the provisioner with count/for_each so it only runs when payload is concrete.","Avoid conditionally-null source/content values."],"tags":["terraform","provisioner","file-provisioner","runtime","config"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}