crowdsecurity/crowdsec · error
unable to symlink parser '%s' to '%s': %w
Error message
unable to symlink parser '%s' to '%s': %w
What it means
installParserItem creates a symlink in runtime/parsers/<stage>/ pointing to the hub copy; if os.Symlink fails with anything other than os.IsExist, it wraps the error. Typical causes are the destination parent directory missing or a filesystem that does not support symlinks.
Source
Thrown at pkg/hubtest/parser.go:40
// runtime/parsers/s00-raw/
itemTypeDirDest := fmt.Sprintf("%s/parsers/%s/", t.RuntimePath, item.Stage)
if err := createDirs([]string{hubDirParserDest, itemTypeDirDest}); err != nil {
return err
}
// runtime/hub/parsers/s00-raw/crowdsecurity/syslog-logs.yaml
hubDirParserPath := filepath.Join(hubDirParserDest, sourceFilename)
if err := Copy(sourcePath, hubDirParserPath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %w", sourcePath, hubDirParserPath, err)
}
// runtime/parsers/s00-raw/syslog-logs.yaml
parserDirParserPath := filepath.Join(itemTypeDirDest, sourceFilename)
if err := os.Symlink(hubDirParserPath, parserDirParserPath); err != nil {
if !os.IsExist(err) {
return fmt.Errorf("unable to symlink parser '%s' to '%s': %w", hubDirParserPath, parserDirParserPath, err)
}
}
return nil
}
func (t *HubTestItem) installParserCustomFrom(parser string, customPath string) (bool, error) {
// we check if its a custom parser
customParserPath := filepath.Join(customPath, parser)
if _, err := os.Stat(customParserPath); os.IsNotExist(err) {
return false, nil
}
customParserPathSplit, customParserName := filepath.Split(customParserPath)
// because path is parsers/<stage>/<author>/parser.yaml and we wan't the stage
splitPath := strings.Split(customParserPathSplit, string(os.PathSeparator))
customParserStage := splitPath[len(splitPath)-3]
View on GitHub (pinned to 909b515798)
Solutions
- Remove the conflicting file at the destination and re-run: `rm runtime/parsers/<stage>/<parser>.yaml`.
- On Windows, enable Developer Mode or run the test with symlink privileges.
- Recreate the runtime tree so installParser creates all parent directories fresh.
- Verify the hub source path exists before symlinking (the copy step succeeded).
Example fix
// before
os.RemoveAll(runtimePath) // leaves stale files if it fails silently
runParserTests()
// after
if err := os.RemoveAll(runtimePath); err != nil {
return fmt.Errorf("could not clean runtime dir: %w", err)
}
runParserTests() Defensive patterns
Strategy: validation
Validate before calling
dst := filepath.Join(runtimePath, "parsers", stage, name+".yaml")
if fi, err := os.Lstat(dst); err == nil {
_ = os.Remove(dst) // clear stale file/symlink before install
}
_ = fi Try / catch
if err := item.InstallParser(name); err != nil {
// on Windows, check symlink privilege / Developer Mode first
return fmt.Errorf("symlink install failed for %s: %w", name, err)
} Prevention
- On Windows, enable Developer Mode or run as a user with symlink privilege.
- Clean the runtime tree before each test run to avoid stale symlinks.
- Verify parent directories exist before symlink-dependent steps.
When it happens
Trigger: os.Symlink(hubDirParserPath, parserDirParserPath) returning an error other than EEXIST: parent dir runtime/parsers/<stage>/ not created, Windows without symlink privilege, stale non-symlink file at the destination path, permission denied.
Common situations: Running hubtests on Windows without developer mode/SeCreateSymbolicLinkPrivilege; a previous failed run left a regular file where the symlink goes; runtime tree partially deleted.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- unable to symlink appsec-rule '%s' to '%s': %w
- unable to symlink postoverflow '%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
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/3b78fe7ccea5422b.
Report an issue: GitHub.