{"record":{"id":"2bc5213bc9ad2a1b","repo":"crowdsecurity/crowdsec","slug":"unable-to-copy-scenario-from-s-to-s-w","errorCode":null,"errorMessage":"unable to copy scenario from '%s' to '%s': %w","messagePattern":"unable to copy scenario from '(.+?)' to '(.+?)': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/hubtest/scenario.go","lineNumber":62,"sourceCode":"}\n\nfunc (t *HubTestItem) installScenarioCustomFrom(scenario string, customPath string) (bool, error) {\n\t// we check if its a custom scenario\n\tcustomScenarioPath := filepath.Join(customPath, scenario)\n\tif _, err := os.Stat(customScenarioPath); os.IsNotExist(err) {\n\t\treturn false, nil\n\t}\n\n\titemTypeDirDest := fmt.Sprintf(\"%s/scenarios/\", t.RuntimePath)\n\tif err := os.MkdirAll(itemTypeDirDest, os.ModePerm); err != nil {\n\t\treturn false, fmt.Errorf(\"unable to create folder '%s': %w\", itemTypeDirDest, err)\n\t}\n\n\tscenarioFileName := filepath.Base(customScenarioPath)\n\n\tscenarioFileDest := filepath.Join(itemTypeDirDest, scenarioFileName)\n\tif err := Copy(customScenarioPath, scenarioFileDest); err != nil {\n\t\treturn false, fmt.Errorf(\"unable to copy scenario from '%s' to '%s': %w\", customScenarioPath, scenarioFileDest, err)\n\t}\n\n\treturn true, nil\n}\n\nfunc (t *HubTestItem) installScenarioCustom(scenario string) error {\n\tfor _, customPath := range t.CustomItemsLocation {\n\t\tfound, err := t.installScenarioCustomFrom(scenario, customPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif found {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"couldn't find custom scenario '%s' in the following location: %+v\", scenario, t.CustomItemsLocation)","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/hubtest/scenario.go#L44-L80","documentation":"After creating the runtime scenarios directory, installScenarioCustomFrom copies the custom scenario file into it via Copy; failure is reported with source and destination paths plus the underlying cause. It means the custom scenario file could not be read or the destination file could not be written.","triggerScenarios":"installScenarioCustom -> installScenarioCustomFrom where Copy(customScenarioPath, scenarioFileDest) fails: source file unreadable/missing (e.g. broken symlink), destination not writable, or disk full.","commonSituations":"CustomItemsLocation points at a directory where the scenario entry is a broken symlink; permission mismatch between the file's owner and the test runner; the destination path already exists as a directory.","solutions":["Verify the source file exists and is readable by the running user (ls -la customScenarioPath).","Fix permissions on the destination runtime/scenarios directory.","If the destination exists as a directory, remove it and re-run.","Check the wrapped error (%w) for the exact syscall failure."],"exampleFix":"// before\nCopy(\"./custom/scen.yaml\", \"/runtime/scenarios/scen.yaml\") // source unreadable\n// after\nchmod +r ./custom/scen.yaml && Copy(\"./custom/scen.yaml\", \"/runtime/scenarios/scen.yaml\")","handlingStrategy":"validation","validationCode":"if fi, err := os.Stat(customScenarioPath); err != nil || fi.IsDir() {\n\treturn fmt.Errorf(\"custom scenario %s missing or a directory\", customScenarioPath)\n}","typeGuard":null,"tryCatchPattern":"if err := Copy(src, dst); err != nil {\n\tvar perr *os.PathError\n\tif errors.As(err, &perr) {\n\t\treturn fmt.Errorf(\"copy failed at %s (op=%s): %w\", perr.Path, perr.Op, err)\n\t}\n\treturn err\n}","preventionTips":["Verify readability of custom scenario files before running tests.","Ensure the destination file is not a directory and is writable.","Keep CustomItemsLocation free of broken symlinks."],"tags":["crowdsec","hub","file-copy","filesystem"],"backgroundTag":"file-write-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}