gofiber/fiber · error · ErrChainCycle

cyclic extractor chain

Error message

cyclic extractor chain

What it means

ErrChainCycle ('cyclic extractor chain', extractors/extractors.go:67) is returned by Chain().Extract (extractors.go:524) when the chain re-enters itself on the same request context. Chain uses a per-chain Locals guard key (set true on entry, cleared on exit); if Extract is invoked again while active, the guard detects the re-entry and short-circuits to prevent infinite recursion.

Source

Thrown at extractors/extractors.go:67

	// SourceQuery indicates the value is extracted from URL query parameters.
	SourceQuery

	// SourceParam indicates the value is extracted from URL path parameters.
	SourceParam

	// SourceCookie indicates the value is extracted from cookies.
	SourceCookie

	// SourceCustom indicates the value is extracted using a custom extractor function.
	SourceCustom
)

// ErrNotFound is returned when the requested value is missing or empty.
var ErrNotFound = errors.New("value not found")

// ErrChainCycle is returned when a chain extractor recursively invokes itself.
var ErrChainCycle = errors.New("cyclic extractor chain")

// Extractor defines a value extraction method with metadata.
type Extractor struct {
	Extract    func(fiber.Ctx) (string, error)
	Key        string      // The parameter/header name used for extraction
	AuthScheme string      // The auth scheme used, e.g., "Bearer"
	Chain      []Extractor // For chained extractors, stores all extractors in the chain
	Source     Source      // The type of source being extracted from
}

// Contains reports whether this extractor, or any extractor in its chain, matches pred.
//
// If pred is nil, Contains returns false.
func (e Extractor) Contains(pred func(Extractor) bool) bool {
	if pred == nil {
		return false
	}

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Ensure each extractor in a Chain resolves to a primitive source (header/cookie/query/form/param) or a distinct custom function, never back to the same chain.
  2. Factor the shared extraction logic into a standalone function and have both call it, rather than chaining the chain into itself.
  3. Add a unit test that calls the chain's Extract twice in sequence to confirm no self-reference exists.

Example fix

// before
chain := extractors.Chain()
chain = extractors.Chain(
    extractors.FromCustom("x", func(c fiber.Ctx) (string, error) {
        return chain.Extract(c) // re-enters itself -> ErrChainCycle
    }),
)

// after
inner := extractors.FromHeader("X-API-Key")
chain := extractors.Chain(
    inner,
    extractors.FromCookie("api_key"),
)
Defensive patterns

Strategy: validation

Validate before calling

// Detect self-referential chains at construction time.
if chain.Contains(func(e extractors.Extractor) bool {
    return e.Extract == nil // placeholder for identity; extend as needed
}) {
    log.Println("warning: inspect chain for self-reference")
}

Try / catch

v, err := chain.Extract(c)
if errors.Is(err, extractors.ErrChainCycle) {
    return fiber.NewError(fiber.StatusInternalServerError, "extractor misconfigured (cycle)")
}

Prevention

When it happens

Trigger: An extractor in a Chain whose Extract function itself calls the same Chain's Extract on the same fiber.Ctx (e.g. a FromCustom function that resolves to the outer chain), or two chains referencing each other transitively. A misbehaving custom extractor that re-dispatches through the parent chain triggers the guard.

Common situations: Building recursive extractor graphs by mistake, or a custom extractor that 'falls back' to the parent chain instead of to a primitive source. Refactoring that accidentally wires a chain into itself.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/65492ed85b27d265.json. Report an issue: GitHub.