crowdsecurity/crowdsec · error

failed to create router for schema ref %s: %w

Error message

failed to create router for schema ref %s: %w

What it means

After the schema validates, LoadSchema builds a route-matching router from the document (openapi3 legacyrouter.NewRouter). If the router cannot be constructed — typically duplicate or conflicting path templates that make routing ambiguous — the error is wrapped as "failed to create router for schema ref <ref>: ..." and the schema is not registered.

Source

Thrown at pkg/appsec/api_validation/api_validation.go:340

	loader := openapi3.NewLoader()
	rv.loaders[ref] = loader

	doc, err := loader.LoadFromData([]byte(schema))
	if err != nil {
		return fmt.Errorf("failed to load schema %s: %w", ref, err)
	}

	// Is it a valid OpenAPI schema?
	// TODO: look into opts, should we expose some of them to the user ?
	if err := doc.Validate(loader.Context, openapi3.DisableExamplesValidation()); err != nil {
		return fmt.Errorf("failed to validate schema %s: %w", ref, err)
	}

	rv.warnUnsupportedSecuritySchemes(ref, doc, options.OnUnsupportedSecurityScheme)

	router, err := legacyrouter.NewRouter(doc)
	if err != nil {
		return fmt.Errorf("failed to create router for schema ref %s: %w", ref, err)
	}

	rv.openAPISchemas[ref] = SchemaData{
		Schema:  doc,
		Router:  router,
		Options: options,
	}

	rv.logger.Infof("loaded schema for ref %s", ref)
	return nil
}

func (rv *RequestValidator) ValidateRequest(ctx context.Context, ref string, r *http.Request) error {
	schemaData, exists := rv.openAPISchemas[ref]
	if !exists {
		return fmt.Errorf("%w: no schema loaded for ref %s", ErrInvalidSchemaName, ref)
	}

View on GitHub (pinned to 909b515798)

Solutions

  1. Read the wrapped router error: it names the conflicting paths — rename parameters so identical-shaped paths use the same parameter name.
  2. Remove or merge duplicate path entries that differ only by parameter name.
  3. Restructure genuinely distinct routes so their templates don't collide (e.g. '/items/by-name/{name}').
  4. If the spec is generated, fix the generator's path naming instead of patching the output.

Example fix

// before
paths:
  /items/{id}:
    get: ...
  /items/{name}:
    get: ...   # conflicts with /items/{id}

// after
paths:
  /items/{id}:
    get: ...
Defensive patterns

Strategy: try-catch

Validate before calling

// detect ambiguous duplicate path shapes before LoadSchema
seen := map[string]string{}
for path := range doc.Paths {
    key := normalizeParams(path) // e.g. /items/{id} and /items/{name} -> /items/{}
    if prev, dup := seen[key]; dup {
        return fmt.Errorf("conflicting paths %q and %q", prev, path)
    }
    seen[key] = path
}

Try / catch

if err := rv.LoadSchema(ref, schema, opts); err != nil {
    if strings.Contains(err.Error(), "failed to create router") {
        log.Errorf("fix conflicting path templates in %s: %v", ref, err)
    }
    return err
}

Prevention

When it happens

Trigger: Loading a spec whose paths contain duplicate route patterns (e.g. '/items/{id}' and '/items/{name}' — same shape, different parameter names), which legacyrouter rejects as ambiguous.

Common situations: Merged/auto-generated specs where two path entries differ only in parameter name; generated clients emitting conflicting routes; concatenating specs from multiple services into one document.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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