{"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/c9def3e214014c1188faabfc4a5bde5095139765/internal/builtin/provisioners/file/resource_provisioner.go#L138-L174","documentation":"Defensive error inside getSrc() at provision-execution time when both 'source' and 'content' evaluate to null. ValidateProvisionerConfig should have caught this earlier, so reaching here means validation was bypassed or the config value was mutated after validation.","triggerScenarios":"The provisioner ProvisionResource path is reached with a config where neither 'content' nor 'source' is set — typically only possible if validation was skipped or a custom provisioner harness feeds a hand-built cty value.","commonSituations":"Programmatic/embedded use of terraform that bypasses ValidateProvisionerConfig; a race where config is cleared between validation and execution.","solutions":["Ensure ValidateProvisionerConfig runs before ProvisionResource (standard terraform CLI always does).","If embedding terraform, explicitly validate the provisioner config object before calling ProvisionResource.","Supply a non-null 'source' or 'content' in the config."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// If embedding terraform, always validate provisioner config before provision:\nvr := p.ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{Config: cfg})\nif vr.Diagnostics.HasErrors() {\n    return vr.Diagnostics.Err()\n}\n// Only then call ProvisionResource.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never skip ValidateProvisionerConfig when embedding terraform.","Treat this error as a bug in integration code, not in user config.","Unit-test the validate-then-provision sequence for custom provisioners."],"tags":["terraform","provisioner","file","defensive","config-validation"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}