hashicorp/packer · error
Error opening shell script: %s
Error message
Error opening shell script: %s
What it means
Raised in the windows-shell provisioner's Provision when a script file selected for provisioning cannot be opened on the host with os.Open. Provision resolves the script list (inline-generated temp script or configured `scripts`), then opens each file to upload it to the guest; any open failure is wrapped as "Error opening shell script: %s" with the original OS error. Unlike the Prepare-time stat check, this runs during the build, so paths that vanished or became unreadable between validation and provisioning land here.
Source
Thrown at provisioner/windows-shell/provisioner.go:187
p.generatedData = generatedData
if p.config.Inline != nil {
temp, err := extractScript(p)
if err != nil {
ui.Error(fmt.Sprintf("Unable to extract inline scripts into a file: %s", err))
}
scripts = append(scripts, temp)
// Remove temp script containing the inline commands when done
defer os.Remove(temp)
}
for _, path := range scripts {
ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path))
log.Printf("Opening %s for reading", path)
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("Error opening shell script: %s", err)
}
defer f.Close()
// Create environment variables to set before executing the command
flattenedVars := p.createFlattenedEnvVars()
// Compile the command
p.config.ctx.Data = &ExecuteCommandTemplate{
Vars: flattenedVars,
Path: p.config.RemotePath,
}
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
return fmt.Errorf("Error processing command: %s", err)
}
// Upload the file and run the command. Do this in the context of
// a single retryable function so that we don't end up withView on GitHub (pinned to eb36e3c3e4)
Solutions
- Re-check the path exists at build time (not just validation): `ls -l <path>` immediately before `packer build` and use `${path.root}` in HCL2 to make paths absolute.
- Fix file permissions so the user running Packer can read the script.
- Ensure no earlier provisioner or cleanup step deletes the script directory mid-build.
- Re-run `packer validate` after any file moves so validation catches it up front.
Example fix
// before: relative path breaks when cwd differs
scripts = ["setup.bat"]
// after
scripts = ["${path.root}/scripts/setup.bat"] Defensive patterns
Strategy: validation
Validate before calling
for _, s := range scripts {
f, err := os.Open(s)
if err != nil { return fmt.Errorf("pre-open check failed for %q: %w", s, err) }
f.Close()
} Try / catch
if strings.Contains(err.Error(), "Error opening shell script") {
// fail fast before guest work; do not retry cloud-side
return fmt.Errorf("host-side script unreadable: %w", err)
} Prevention
- Use ${path.root}-anchored absolute paths in templates.
- Don't mutate or clean the scripts directory during the build.
- Verify file read permissions for the packer process user.
- Keep scripts inside the repo working tree committed for CI.
When it happens
Trigger: Provision loops over `scripts` and calls os.Open(path) for each; the call returns an error because the file was deleted or renamed after Prepare, permissions changed, or the path is a directory — provisioner.go:185-189. Also triggered for the temp script path if extractScript output was removed mid-build.
Common situations: Script deleted by a prior build step or tmp cleaner between validation and provisioning; relative path broken because the build changed working directory (e.g. running packer from a different cwd than intended); permissions tightened on a shared CI workspace; antivirus locking/removing the file.
Related errors
- Bad script '%s': %s
- Only one of script or scripts can be specified.
- Either a script file or inline script must be specified.
- failed to create temporary file in user SBOM directory %s: %
- failed to create destination directory for user SBOM: %s
AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05).
Data as JSON: /api/errors/441d2e63e10e6322.
Report an issue: GitHub.