crowdsecurity/crowdsec · error

unable to copy scenario from '%s' to '%s': %w

Error message

unable to copy scenario from '%s' to '%s': %w

What it means

After creating the runtime scenarios directory, installScenarioCustomFrom copies the custom scenario file into it via Copy; failure is reported with source and destination paths plus the underlying cause. It means the custom scenario file could not be read or the destination file could not be written.

Source

Thrown at pkg/hubtest/scenario.go:62

}

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

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

	scenarioFileName := filepath.Base(customScenarioPath)

	scenarioFileDest := filepath.Join(itemTypeDirDest, scenarioFileName)
	if err := Copy(customScenarioPath, scenarioFileDest); err != nil {
		return false, fmt.Errorf("unable to copy scenario from '%s' to '%s': %w", customScenarioPath, scenarioFileDest, err)
	}

	return true, nil
}

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

		if found {
			return nil
		}
	}

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

View on GitHub (pinned to 909b515798)

Solutions

  1. Verify the source file exists and is readable by the running user (ls -la customScenarioPath).
  2. Fix permissions on the destination runtime/scenarios directory.
  3. If the destination exists as a directory, remove it and re-run.
  4. Check the wrapped error (%w) for the exact syscall failure.

Example fix

// before
Copy("./custom/scen.yaml", "/runtime/scenarios/scen.yaml") // source unreadable
// after
chmod +r ./custom/scen.yaml && Copy("./custom/scen.yaml", "/runtime/scenarios/scen.yaml")
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(customScenarioPath); err != nil || fi.IsDir() {
	return fmt.Errorf("custom scenario %s missing or a directory", customScenarioPath)
}

Try / catch

if err := Copy(src, dst); err != nil {
	var perr *os.PathError
	if errors.As(err, &perr) {
		return fmt.Errorf("copy failed at %s (op=%s): %w", perr.Path, perr.Op, err)
	}
	return err
}

Prevention

When it happens

Trigger: installScenarioCustom -> installScenarioCustomFrom where Copy(customScenarioPath, scenarioFileDest) fails: source file unreadable/missing (e.g. broken symlink), destination not writable, or disk full.

Common situations: CustomItemsLocation points at a directory where the scenario entry is a broken symlink; permission mismatch between the file's owner and the test runner; the destination path already exists as a directory.

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/2bc5213bc9ad2a1b. Report an issue: GitHub.