{"record":{"id":"50fd201227ce3ce3","repo":"crowdsecurity/crowdsec","slug":"unable-to-symlink-scenario-s-to-s-w","errorCode":null,"errorMessage":"unable to symlink scenario '%s' to '%s': %w","messagePattern":"unable to symlink scenario '(.+?)' to '(.+?)': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/hubtest/scenario.go","lineNumber":39,"sourceCode":"\n\t// runtime/parsers/scenarios/\n\titemTypeDirDest := fmt.Sprintf(\"%s/scenarios/\", t.RuntimePath)\n\n\tif err := createDirs([]string{hubDirScenarioDest, itemTypeDirDest}); err != nil {\n\t\treturn err\n\t}\n\n\t// runtime/hub/scenarios/crowdsecurity/ssh-bf.yaml\n\thubDirScenarioPath := filepath.Join(hubDirScenarioDest, sourceFilename)\n\tif err := Copy(sourcePath, hubDirScenarioPath); err != nil {\n\t\treturn fmt.Errorf(\"unable to copy '%s' to '%s': %w\", sourcePath, hubDirScenarioPath, err)\n\t}\n\n\t// runtime/scenarios/ssh-bf.yaml\n\tscenarioDirParserPath := filepath.Join(itemTypeDirDest, sourceFilename)\n\tif err := os.Symlink(hubDirScenarioPath, scenarioDirParserPath); err != nil {\n\t\tif !os.IsExist(err) {\n\t\t\treturn fmt.Errorf(\"unable to symlink scenario '%s' to '%s': %w\", hubDirScenarioPath, scenarioDirParserPath, err)\n\t\t}\n\t}\n\n\treturn nil\n}\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","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/hubtest/scenario.go#L21-L57","documentation":"After copying the scenario into the runtime hub directory, installScenarioItem creates a symlink from the item-type directory (runtime/scenarios/<name>.yaml) to the hub copy. If os.Symlink fails for any reason other than the link already existing, this error wraps it. Existing symlinks are intentionally tolerated.","triggerScenarios":"installScenario -> installScenarioItem where a symlink must be created at runtime/scenarios/<file> but fails: the destination path exists as a regular file/directory, or the filesystem doesn't support symlinks, or permission denied.","commonSituations":"A previous test run left a real file (not a symlink) at the destination; running on Windows without symlink privileges; read-only runtime directory.","solutions":["Remove the stale non-symlink file at the destination path and re-run.","On Windows, run with symlink privileges (developer mode/admin) or on a filesystem supporting symlinks.","Check permissions on the runtime scenarios directory.","Clean the test runtime directory (rm -rf) and re-run the tests."],"exampleFix":"// before\nos.WriteFile(\"/runtime/scenarios/ssh-bf.yaml\", data, 0644) // later install tries to symlink same path\n// after\nos.RemoveAll(\"/runtime/scenarios/ssh-bf.yaml\") // then os.Symlink succeeds","handlingStrategy":"validation","validationCode":"if fi, err := os.Lstat(dest); err == nil && fi.Mode()&os.ModeSymlink == 0 {\n\tos.Remove(dest) // clear stale non-symlink before install\n}","typeGuard":null,"tryCatchPattern":"if err := os.Symlink(target, dest); err != nil && !os.IsExist(err) {\n\tvar le *os.LinkError\n\tif errors.As(err, &le) {\n\t\treturn fmt.Errorf(\"symlink unsupported or blocked at %s: %w\", dest, err)\n\t}\n\treturn err\n}","preventionTips":["Clean the runtime test directory between runs (rm -rf runtime/).","On Windows enable Developer Mode or run in WSL so symlinks work.","Never write regular files into the runtime scenarios directory manually."],"tags":["crowdsec","hub","symlink","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"}