crowdsecurity/crowdsec · error
unable to symlink appsec-rule '%s' to '%s': %w
Error message
unable to symlink appsec-rule '%s' to '%s': %w
What it means
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.
Source
Thrown at pkg/hubtest/appsecrule.go:42
// runtime/appsec-rules/
itemTypeDirDest := fmt.Sprintf("%s/appsec-rules/", t.RuntimePath)
if err := createDirs([]string{hubDirAppsecRuleDest, itemTypeDirDest}); err != nil {
return err
}
// runtime/hub/appsec-rules/crowdsecurity/rule.yaml
hubDirAppsecRulePath := filepath.Join(itemTypeDirDest, sourceFilename)
if err := Copy(sourcePath, hubDirAppsecRulePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %w", sourcePath, hubDirAppsecRulePath, err)
}
// runtime/appsec-rules/rule.yaml
appsecRulePath := filepath.Join(itemTypeDirDest, sourceFilename)
if err := os.Symlink(hubDirAppsecRulePath, appsecRulePath); err != nil {
if !os.IsExist(err) {
return fmt.Errorf("unable to symlink appsec-rule '%s' to '%s': %w", hubDirAppsecRulePath, appsecRulePath, err)
}
}
return nil
}
func (t *HubTestItem) installAppsecRuleCustomFrom(appsecrule string, customPath string) (bool, error) {
// we check if its a custom appsec-rule
customAppsecRulePath := filepath.Join(customPath, appsecrule)
if _, err := os.Stat(customAppsecRulePath); os.IsNotExist(err) {
return false, nil
}
customAppsecRulePathSplit := strings.Split(customAppsecRulePath, "/")
customAppsecRuleName := customAppsecRulePathSplit[len(customAppsecRulePathSplit)-1]
itemTypeDirDest := fmt.Sprintf("%s/appsec-rules/", t.RuntimePath)
if err := os.MkdirAll(itemTypeDirDest, os.ModePerm); err != nil {View on GitHub (pinned to 909b515798)
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
Example fix
# before: leftover regular file blocks the link ls -la /tmp/runtime/appsec-rules/rule.yaml # -rw-r--r-- regular file # after rm /tmp/runtime/appsec-rules/rule.yaml && re-run test setup
Defensive patterns
Strategy: try-catch
Validate before calling
dest := filepath.Join(t.RuntimePath, "appsec-rules", filename)
if fi, err := os.Lstat(dest); err == nil && fi.Mode()&os.ModeSymlink == 0 {
// regular file in the way: remove it first
os.Remove(dest)
} Try / catch
err := t.installAppsecRule(name)
if err != nil && strings.Contains(err.Error(), "unable to symlink") {
var le *fs.PathError
if errors.As(err, &le) {
// EPERM/EEXIST handling: clean stale entry or fall back to copy
}
} Prevention
- 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)
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- unable to symlink parser '%s' to '%s': %w
- too many levels of symbolic links
- while getting owner security info: %w
- no DACL found on plugin, meaning fully permissive access on
- while getting security info: %w
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/fe8b0273cef4ded7.
Report an issue: GitHub.