crowdsecurity/crowdsec · error

unable to symlink postoverflow '%s' to '%s': %w

Error message

unable to symlink postoverflow '%s' to '%s': %w

What it means

After copying the item into the runtime hub, installPostoverflowItem creates a symlink from the runtime parser/postoverflow directory to the hub copy. If os.Symlink fails for a reason other than the link already existing, the error is wrapped with both paths. An existing symlink is tolerated (os.IsExist check).

Source

Thrown at pkg/hubtest/postoverflow.go:40

	// runtime/postoverflows/s00-enrich
	itemTypeDirDest := fmt.Sprintf("%s/postoverflows/%s/", t.RuntimePath, item.Stage)

	if err := createDirs([]string{hubDirPostoverflowDest, itemTypeDirDest}); err != nil {
		return err
	}

	// runtime/hub/postoverflows/s00-enrich/crowdsecurity/rdns.yaml
	hubDirPostoverflowPath := filepath.Join(hubDirPostoverflowDest, sourceFilename)
	if err := Copy(sourcePath, hubDirPostoverflowPath); err != nil {
		return fmt.Errorf("unable to copy '%s' to '%s': %w", sourcePath, hubDirPostoverflowPath, err)
	}

	// runtime/postoverflows/s00-enrich/rdns.yaml
	postoverflowDirParserPath := filepath.Join(itemTypeDirDest, sourceFilename)
	if err := os.Symlink(hubDirPostoverflowPath, postoverflowDirParserPath); err != nil {
		if !os.IsExist(err) {
			return fmt.Errorf("unable to symlink postoverflow '%s' to '%s': %w", hubDirPostoverflowPath, postoverflowDirParserPath, err)
		}
	}

	return nil
}

func (t *HubTestItem) installPostoverflowCustomFrom(postoverflow string, customPath string) (bool, error) {
	// we check if its a custom postoverflow
	customPostOverflowPath := filepath.Join(customPath, postoverflow)
	if _, err := os.Stat(customPostOverflowPath); os.IsNotExist(err) {
		return false, nil
	}

	customPostOverflowPathSplit := strings.Split(customPostOverflowPath, "/")
	customPostoverflowName := customPostOverflowPathSplit[len(customPostOverflowPathSplit)-1]
	// because path is postoverflows/<stage>/<author>/parser.yaml and we wan't the stage
	customPostoverflowStage := customPostOverflowPathSplit[len(customPostOverflowPathSplit)-3]

View on GitHub (pinned to 909b515798)

Solutions

  1. Ensure the itemTypeDirDest directory exists before install (check earlier MkdirAll results)
  2. Remove the stale non-symlink file at the destination path and re-run
  3. On Windows, run with privileges or enable developer mode for symlink support
  4. Check the wrapped error (EACCES/ENOENT) and fix the underlying path/permission issue

Example fix

// before
if err := os.Symlink(hubDirPostoverflowPath, postoverflowDirParserPath); err != nil {
    return fmt.Errorf("unable to symlink postoverflow '%s' to '%s': %w", ...)
}
// after
_ = os.Remove(postoverflowDirParserPath)
if err := os.Symlink(hubDirPostoverflowPath, postoverflowDirParserPath); err != nil {
    return fmt.Errorf("unable to symlink postoverflow '%s' to '%s': %w", ...)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(filepath.Dir(linkPath)); err != nil {
    return fmt.Errorf("symlink parent missing: %w", err)
}
if fi, err := os.Lstat(linkPath); err == nil && fi.Mode()&os.ModeSymlink == 0 {
    os.Remove(linkPath) // stale non-symlink file
}

Try / catch

if err := t.installPostoverflow(item); err != nil {
    if strings.Contains(err.Error(), "unable to symlink") && runtime.GOOS == "windows" {
        return fmt.Errorf("symlinks need admin/developer mode on windows: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: os.Symlink(hubDirPostoverflowPath, postoverflowDirParserPath) fails: parent directory of the symlink target doesn't exist, permission denied, or on Windows where symlink creation requires privileges.

Common situations: Runtime postoverflow stage directory not created before symlinking; running hub tests on Windows without developer mode/privileges; stale file at the symlink destination that is not a symlink.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/7b5b06d2fa33fc11. Report an issue: GitHub.