crowdsecurity/crowdsec · error

stage '%s' extracted from '%s' doesn't exist in the hub

Error message

stage '%s' extracted from '%s' doesn't exist in the hub

What it means

installParserCustomFrom expects custom parser files to live in a path where the third-from-last path segment is a stage name that exists in the hub (e.g. .../parsers/s00-raw/name.yaml). When the extracted stage directory does not exist under <HubPath>/parsers, the function refuses to install and returns this error.

Source

Thrown at pkg/hubtest/parser.go:62

	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]

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

View on GitHub (pinned to 909b515798)

Solutions

  1. Move/rename the custom parser so its parent structure is <path>/parsers/<existing-stage>/<name>.yaml with a stage that exists in the hub (`ls <HubPath>/parsers`).
  2. Check the stage name spelling against hub stages (s00-raw, s01-parse, ...).
  3. If it's not a custom parser but a hub item, use the normal installParser path instead.

Example fix

// before
custom := "./mytests/parsers/s05-typo/my.yaml"
// after
custom := "./mytests/parsers/s01-parse/my.yaml" // stage must exist in hub parsers/
Defensive patterns

Strategy: validation

Validate before calling

stage := filepath.Base(filepath.Dir(filepath.Dir(customParserPath)))
if _, err := os.Stat(filepath.Join(hubPath, "parsers", stage)); err != nil {
	return fmt.Errorf("stage %s does not exist in hub; valid stages: %v", stage, hubStages)
}

Try / catch

if err := item.InstallParserCustom(name); err != nil {
	// check the custom parser's directory layout matches <...>/parsers/<stage>/<file>.yaml
	return err
}

Prevention

When it happens

Trigger: Calling installParserCustom with a custom parser path whose directory layout places a bogus/nonexistent stage two levels up (e.g. .../parsers/s02-unknown/my-parser.yaml) or a path with too few segments so the wrong segment is picked as the stage.

Common situations: Renaming or restructuring a custom parser directory without matching a real hub stage; typo in the stage name (s01-raw vs s00-raw); putting the parser file directly in runtime/parsers/ without a stage directory.

Related errors


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