hashicorp/terraform · error

Cannot set both 'source' and 'content'

Error message

Cannot set both 'source' and 'content'

What it means

Thrown by the file provisioner's ValidateProvisionerConfig (resource_provisioner.go:69). The file provisioner copies a single file/dir from a local 'source' OR writes inline 'content'; both arguments are mutually exclusive. If both source and content are non-null in the provisioner block, validation appends this error and returns immediately, failing the config validation phase before any apply.

Solutions

  1. Remove either the `source` or the `content` argument from the provisioner block so exactly one is set.
  2. If you need both behaviors, split into two separate file provisioner blocks.

Example fix

# before
provisioner "file" {
  source  = "./app.conf"
  content = "key=value"
  destination = "/etc/app/app.conf"
}
# after
provisioner "file" {
  source      = "./app.conf"
  destination = "/etc/app/app.conf"
}
Defensive patterns

Strategy: validation

Validate before calling

// Before applying, validate the file provisioner sets exactly one payload source.
func validateFileProvisioner(src, content string) error {
    if src != "" && content != "" {
        return errors.New("Cannot set both 'source' and 'content'")
    }
    return nil
}

Type guard

type fileProvisionerArgs struct {
    Source      optional[string]
    Content     optional[string]
    Destination string
}
func (a fileProvisionerArgs) valid() bool {
    return a.Destination != "" && a.Source.set != a.Content.set // exactly one
}

Prevention

When it happens

Trigger: A provisioner "file" block whose HCL sets both `source = ...` and `content = ...`. The coercion is done via the provisioner schema; cfg.GetAttr("source") and cfg.GetAttr("content") are both non-null, hitting the first switch case.

Common situations: Copy-pasting examples that combine a source path with inline content. Refactoring a file provisioner from source-based to content-based and forgetting to remove the old attribute.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/51edd95951ef778b. Report an issue: GitHub.

Appendix: source

Thrown at internal/builtin/provisioners/file/resource_provisioner.go:69

			},
		},
	}
	resp.Provisioner = schema
	return resp
}

func (p *provisioner) ValidateProvisionerConfig(req provisioners.ValidateProvisionerConfigRequest) (resp provisioners.ValidateProvisionerConfigResponse) {
	cfg, err := p.GetSchema().Provisioner.CoerceValue(req.Config)
	if err != nil {
		resp.Diagnostics = resp.Diagnostics.Append(err)
	}

	source := cfg.GetAttr("source")
	content := cfg.GetAttr("content")

	switch {
	case !source.IsNull() && !content.IsNull():
		resp.Diagnostics = resp.Diagnostics.Append(errors.New("Cannot set both 'source' and 'content'"))
		return resp
	case source.IsNull() && content.IsNull():
		resp.Diagnostics = resp.Diagnostics.Append(errors.New("Must provide one of 'source' or 'content'"))
		return resp
	}

	return resp
}

func (p *provisioner) ProvisionResource(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
	if req.Connection.IsNull() {
		resp.Diagnostics = resp.Diagnostics.Append(tfdiags.WholeContainingBody(
			tfdiags.Error,
			"file provisioner error",
			"Missing connection configuration for provisioner.",
		))
		return resp
	}

View on GitHub (pinned to d32a084675)