{"record":{"id":"e4c1c29c36e882a3","repo":"hashicorp/packer","slug":"bad-source-s-s","errorCode":null,"errorMessage":"Bad source '%s': %s","messagePattern":"Bad source '(.+?)': (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"provisioner/file/provisioner.go","lineNumber":111,"sourceCode":"\tif p.config.Direction == \"\" {\n\t\tp.config.Direction = \"upload\"\n\t}\n\n\tvar errs *packersdk.MultiError\n\n\tif p.config.Direction != \"download\" && p.config.Direction != \"upload\" {\n\t\terrs = packersdk.MultiErrorAppend(errs,\n\t\t\terrors.New(\"Direction must be one of: download, upload.\"))\n\t}\n\tif p.config.Source != \"\" {\n\t\tp.config.Sources = append(p.config.Sources, p.config.Source)\n\t}\n\n\tif p.config.Direction == \"upload\" {\n\t\tfor _, src := range p.config.Sources {\n\t\t\tif _, err := os.Stat(src); p.config.Generated == false && err != nil {\n\t\t\t\terrs = packersdk.MultiErrorAppend(errs,\n\t\t\t\t\tfmt.Errorf(\"Bad source '%s': %s\", src, err))\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(p.config.Sources) > 0 && p.config.Content != \"\" {\n\t\terrs = packersdk.MultiErrorAppend(errs,\n\t\t\terrors.New(\"source(s) conflicts with content.\"))\n\t}\n\n\tif p.config.Destination == \"\" {\n\t\terrs = packersdk.MultiErrorAppend(errs,\n\t\t\terrors.New(\"Destination must be specified.\"))\n\t}\n\n\tif errs != nil && len(errs.Errors) > 0 {\n\t\treturn errs\n\t}\n","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/provisioner/file/provisioner.go#L93-L129","documentation":"During Prepare of the file provisioner (direction=upload), each configured source is stat'ed on the host. When generated=false (the default, expressed here as == false) and os.Stat fails — usually the file does not exist — this error is appended to the validation errors. The %s carries the underlying OS error (e.g. 'no such file or directory').","triggerScenarios":"file provisioner with direction \"upload\" (the default), a source path that does not exist on the build host at template-validation time, and generated not set to true.","commonSituations":"Typo in the source path; the file is produced later by a previous provisioner or build step but `generated` was not set; relative path resolved from a different working directory; path valid on the remote but being used for upload.","solutions":["Create the source file or fix the path before running packer","Set generated = true if the file is produced at runtime by generated data / a prior provisioner so the stat check is skipped","Use an absolute path or correct the relative path relative to the template's directory","Switch direction to download if you actually meant to fetch a file from the machine"],"exampleFix":"// before\nprovisioner file {\n  source      = \"app.tar.gz\"\n  destination = \"/tmp/app.tar.gz\"\n}\n// after\nprovisioner file {\n  source      = \"app.tar.gz\"\n  destination = \"/tmp/app.tar.gz\"\n  generated   = true\n}","handlingStrategy":"validation","validationCode":"// Pre-validate upload sources in shell before packer build:\nfor f in $SOURCES; do test -e \"$f\" || echo \"missing: $f\"; done\n// In Go config validation:\nfor _, src := range cfg.Sources {\n    if _, err := os.Stat(src); err != nil && !cfg.Generated {\n        fmt.Printf(\"source will fail validation: %s (%v)\\n\", src, err)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run packer validate before build — this error is caught at Prepare time","Set generated = true for files created at build time rather than pre-existing on disk","Use absolute paths anchored to the template directory","Verify direction: stat checks only apply to upload"],"tags":["packer","file-provisioner","validation","config"],"backgroundTag":"source-file-not-found","analyzedSha":"eb36e3c3e48a036f3e8cc94087636ee72e1303c9","analyzedAt":"2026-09-05T13:20:43.127Z","contentChangedAt":"2026-09-05T13:20:43.127Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}