{"record":{"id":"fe8b0273cef4ded7","repo":"crowdsecurity/crowdsec","slug":"unable-to-symlink-appsec-rule-s-to-s-w","errorCode":null,"errorMessage":"unable to symlink appsec-rule '%s' to '%s': %w","messagePattern":"unable to symlink appsec-rule '(.+?)' to '(.+?)': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/hubtest/appsecrule.go","lineNumber":42,"sourceCode":"\n\t// runtime/appsec-rules/\n\titemTypeDirDest := fmt.Sprintf(\"%s/appsec-rules/\", t.RuntimePath)\n\n\tif err := createDirs([]string{hubDirAppsecRuleDest, itemTypeDirDest}); err != nil {\n\t\treturn err\n\t}\n\n\t// runtime/hub/appsec-rules/crowdsecurity/rule.yaml\n\thubDirAppsecRulePath := filepath.Join(itemTypeDirDest, sourceFilename)\n\tif err := Copy(sourcePath, hubDirAppsecRulePath); err != nil {\n\t\treturn fmt.Errorf(\"unable to copy '%s' to '%s': %w\", sourcePath, hubDirAppsecRulePath, err)\n\t}\n\n\t// runtime/appsec-rules/rule.yaml\n\tappsecRulePath := filepath.Join(itemTypeDirDest, sourceFilename)\n\tif err := os.Symlink(hubDirAppsecRulePath, appsecRulePath); err != nil {\n\t\tif !os.IsExist(err) {\n\t\t\treturn fmt.Errorf(\"unable to symlink appsec-rule '%s' to '%s': %w\", hubDirAppsecRulePath, appsecRulePath, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (t *HubTestItem) installAppsecRuleCustomFrom(appsecrule string, customPath string) (bool, error) {\n\t// we check if its a custom appsec-rule\n\tcustomAppsecRulePath := filepath.Join(customPath, appsecrule)\n\tif _, err := os.Stat(customAppsecRulePath); os.IsNotExist(err) {\n\t\treturn false, nil\n\t}\n\n\tcustomAppsecRulePathSplit := strings.Split(customAppsecRulePath, \"/\")\n\tcustomAppsecRuleName := customAppsecRulePathSplit[len(customAppsecRulePathSplit)-1]\n\n\titemTypeDirDest := fmt.Sprintf(\"%s/appsec-rules/\", t.RuntimePath)\n\tif err := os.MkdirAll(itemTypeDirDest, os.ModePerm); err != nil {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/hubtest/appsecrule.go#L24-L60","documentation":"installAppsecRuleItem creates a symlink from runtime/appsec-rules/<name> to the copy inside the runtime hub dir; on failure (other than the link already existing) it wraps os.Symlink's error. It means the runtime symlink could not be created.","triggerScenarios":"installAppsecRule on a hub item where appsecRulePath already exists as a real file (not a link), the destination directory lacks write permission, or the filesystem does not support symlinks (Windows without privileges, FAT/exFAT mounts, some network shares).","commonSituations":"A previous test run left a regular file where the symlink should go; running tests on a Windows checkout without symlink support; destination on a filesystem where symlinking is not permitted.","solutions":["Remove the stale file/symlink at the runtime appsec-rules destination path and re-run","Run on a filesystem/user that supports symlink creation (privileged account on Windows, non-FAT filesystem)","Pre-create the link manually pointing to the runtime-hub copy if symlinks are impossible in the environment"],"exampleFix":"# before: leftover regular file blocks the link\nls -la /tmp/runtime/appsec-rules/rule.yaml   # -rw-r--r-- regular file\n# after\nrm /tmp/runtime/appsec-rules/rule.yaml && re-run test setup","handlingStrategy":"try-catch","validationCode":"dest := filepath.Join(t.RuntimePath, \"appsec-rules\", filename)\nif fi, err := os.Lstat(dest); err == nil && fi.Mode()&os.ModeSymlink == 0 {\n\t// regular file in the way: remove it first\n\tos.Remove(dest)\n}","typeGuard":null,"tryCatchPattern":"err := t.installAppsecRule(name)\nif err != nil && strings.Contains(err.Error(), \"unable to symlink\") {\n\tvar le *fs.PathError\n\tif errors.As(err, &le) {\n\t\t// EPERM/EEXIST handling: clean stale entry or fall back to copy\n\t}\n}","preventionTips":["Clean the runtime directory between test runs (rm -rf runtime/appsec-rules)","Enable Windows Developer Mode or run privileged if tests need symlinks on Windows","Keep test workspaces on symlink-capable filesystems (ext4/NTFS, not FAT/network shares)"],"tags":["filesystem","symlink","windows"],"backgroundTag":"symlink-creation-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}