crowdsecurity/crowdsec · error

unable to copy custom parser '%s' to '%s': %w

Error message

unable to copy custom parser '%s' to '%s': %w

What it means

installParserCustomFrom copies the custom parser file into the freshly created stage directory with Copy and wraps the error. The stage directory exists at this point, so failures come from reading the source or writing the destination file.

Source

Thrown at pkg/hubtest/parser.go:73

	// 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]

	// check if stage exist
	hubStagePath := filepath.Join(t.HubPath, fmt.Sprintf("parsers/%s", customParserStage))
	if _, err := os.Stat(hubStagePath); os.IsNotExist(err) {
		return false, fmt.Errorf("stage '%s' extracted from '%s' doesn't exist in the hub", customParserStage, hubStagePath)
	}

	stageDirDest := fmt.Sprintf("%s/parsers/%s/", t.RuntimePath, customParserStage)
	if err := os.MkdirAll(stageDirDest, os.ModePerm); err != nil {
		return false, fmt.Errorf("unable to create folder '%s': %w", stageDirDest, err)
	}

	customParserDest := filepath.Join(stageDirDest, customParserName)
	// if path to parser exist, copy it
	if err := Copy(customParserPath, customParserDest); err != nil {
		return false, fmt.Errorf("unable to copy custom parser '%s' to '%s': %w", customParserPath, customParserDest, err)
	}

	return true, nil
}

func (t *HubTestItem) installParserCustom(parser string) error {
	for _, customPath := range t.CustomItemsLocation {
		found, err := t.installParserCustomFrom(parser, customPath)
		if err != nil {
			return err
		}

		if found {
			return nil
		}
	}

	return fmt.Errorf("couldn't find custom parser '%s' in the following locations: %+v", parser, t.CustomItemsLocation)

View on GitHub (pinned to 909b515798)

Solutions

  1. Confirm the custom parser source file exists and is readable: `ls -l <customParserPath>`.
  2. Ensure the destination stage dir is writable by the test user.
  3. Pass the file path (not a directory) as the custom parser location.

Example fix

// before
installParserCustom("./parsers/s01-parse") // directory passed
// after
installParserCustom("./parsers/s01-parse/my-parser.yaml") // file path
Defensive patterns

Strategy: validation

Validate before calling

fi, err := os.Stat(customParserPath)
if err != nil {
	return fmt.Errorf("custom parser %s missing: %w", customParserPath, err)
}
if fi.IsDir() {
	return fmt.Errorf("custom parser path %s is a directory, pass the file", customParserPath)
}

Try / catch

if err := item.InstallParserCustom(name); err != nil {
	// verify source file readability and stage dir writability
	return err
}

Prevention

When it happens

Trigger: Copy(customParserPath, customParserDest) failing: the source custom parser file doesn't exist or is unreadable, destination stage dir not writable, or customParserName resolves to an invalid filename.

Common situations: Custom parser deleted/moved between the stat check and the copy; source path is a directory rather than a file; disk full; stage directory name derived from a path with odd characters.

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


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