hashicorp/terraform · error
Failed to open script '%s': %v
Error message
Failed to open script '%s': %v
What it means
Returned by the script-reading helper in remote-exec at resource_provisioner.go:221 when os.Open(s) fails for one of the local script paths declared in scripts = [...]. The %s is the offending path and %v the OS error. Open handles are closed before returning, so this is a clean early failure before any remote activity.
Source
Thrown at internal/builtin/provisioners/remote-exec/resource_provisioner.go:221
return nil, errors.New("invalid null string in 'script'")
}
s := script.AsString()
if s == "" {
return nil, errors.New("invalid empty string in 'script'")
}
scripts = append(scripts, s)
}
}
// Open all the scripts
var fhs []io.ReadCloser
for _, s := range scripts {
fh, err := os.Open(s)
if err != nil {
for _, fh := range fhs {
fh.Close()
}
return nil, fmt.Errorf("Failed to open script '%s': %v", s, err)
}
fhs = append(fhs, fh)
}
// Done, return the file handles
return fhs, nil
}
// runScripts is used to copy and execute a set of scripts
func runScripts(ctx context.Context, o provisioners.UIOutput, comm communicator.Communicator, scripts []io.ReadCloser) error {
retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout())
defer cancel()
// Wait and retry until we establish the connection
err := communicator.Retry(retryCtx, func() error {
return comm.Connect(o)
})
if err != nil {View on GitHub (pinned to c9def3e214)
Solutions
- Use an absolute path or verify the path resolves relative to the Terraform configuration directory.
- Ensure the script file exists in the repo/workspace and is readable.
- Check file permissions (chmod +r) for the user running Terraform.
Example fix
// before
provisioner "remote-exec" { scripts = ["scripts/setup.sh"] } // Failed to open script
// after
provisioner "remote-exec" { scripts = ["${path.module}/scripts/setup.sh"] } Defensive patterns
Strategy: validation
Validate before calling
// Validate every script path in the provisioner config before running.
for _, s := range scripts {
if _, err := os.Stat(s); err != nil {
return fmt.Errorf("script %q missing: %w", s, err)
}
} Prevention
- Use ${path.module}/... for script paths so they resolve regardless of cwd.
- Commit script files to the repo and run terraform fmt/validate in CI.
When it happens
Trigger: A remote-exec provisioner with scripts = ["./run.sh"] where the file does not exist, is unreadable, or the path is wrong relative to the Terraform working directory.
Common situations: Relative path resolved against the wrong cwd; script file not committed to the repo; file permissions deny the Terraform process; typo in the path.
Related errors
- Failed to upload script: %v
- Error starting script: %v
- invalid null string in 'scripts'
- invalid empty string in 'scripts'
- invalid empty string in 'script'
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/2fe97fdf3c0a0b7c.
Report an issue: GitHub.