{"record":{"id":"65492ed85b27d265","repo":"gofiber/fiber","slug":"cyclic-extractor-chain","errorCode":null,"errorMessage":"cyclic extractor chain","messagePattern":"cyclic extractor chain","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extractors/extractors.go","lineNumber":68,"sourceCode":"\n\t// SourceQuery indicates the value is extracted from URL query parameters.\n\tSourceQuery\n\n\t// SourceParam indicates the value is extracted from URL path parameters.\n\tSourceParam\n\n\t// SourceCookie indicates the value is extracted from cookies.\n\tSourceCookie\n\n\t// SourceCustom indicates the value is extracted using a custom extractor function.\n\tSourceCustom\n)\n\n// ErrNotFound is returned when the requested value is missing or empty.\nvar ErrNotFound = errors.New(\"value not found\")\n\n// ErrChainCycle is returned when a chain extractor recursively invokes itself.\nvar ErrChainCycle = errors.New(\"cyclic extractor chain\")\n\n// Extractor defines a value extraction method with metadata.\ntype Extractor struct {\n\tExtract    func(fiber.Ctx) (string, error)\n\tKey        string      // The parameter/header name used for extraction\n\tAuthScheme string      // The auth scheme used, e.g., \"Bearer\"\n\tChain      []Extractor // For chained extractors, stores all extractors in the chain\n\tSource     Source      // The type of source being extracted from\n}\n\n// Contains reports whether this extractor, or any extractor in its chain, matches pred.\n//\n// If pred is nil, Contains returns false.\nfunc (e Extractor) Contains(pred func(Extractor) bool) bool {\n\tif pred == nil {\n\t\treturn false\n\t}\n","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/extractors/extractors.go#L50-L86","documentation":"ErrChainCycle is returned by the Chain extractor (extractors/extractors.go:537) when it detects that the same chain has been re-entered recursively within a single request. Each Chain call mints a unique guard key stored in c.Locals; on re-entry the guard is already true and the Extract closure short-circuits with the sentinel instead of looping forever.","triggerScenarios":"Building a Chain whose inner list contains the Chain itself (directly or transitively), or a custom extractor (FromCustom) that calls Extract on its enclosing chain. On the first re-entry within the same request, the Locals guard at extractors.go:534-541 fires.","commonSituations":"Constructing a fallback chain where the tail accidentally references the head; a custom extractor that calls a parent extractor to delegate; refactoring a linear chain into a recursive structure without breaking the cycle.","solutions":["Break the cycle: build each Chain from leaf extractors only, never from itself.","If a custom extractor must delegate, pass the specific inner extractor rather than the enclosing Chain.","Add a unit test that builds the chain and asserts Extract does not return ErrChainCycle on a representative request."],"exampleFix":"// before\nchain := extractors.Chain(extA, extB)\n// later, accidentally:\nchain = extractors.Chain(chain, extC) // re-entry\n// after\nchain := extractors.Chain(extA, extB, extC)","handlingStrategy":"validation","validationCode":"// Assert at construction that no Chain references itself, directly or\n// transitively. A simple identity check catches the common direct case.\nfunc buildChain(leaf extractors.Extractor, rest ...extractors.Extractor) extractors.Extractor {\n    for _, e := range rest {\n        if sameExtractor(e, leaf) {\n            panic(\"chain cycle: leaf re-entered\")\n        }\n    }\n    return extractors.Chain(append([]extractors.Extractor{leaf}, rest...)...)\n}","typeGuard":"// isChainCycle reports whether a Chain detected recursive re-entry.\nfunc isChainCycle(err error) bool {\n    return errors.Is(err, extractors.ErrChainCycle)\n}","tryCatchPattern":"val, err := chain.Extract(c)\nif err != nil {\n    if isChainCycle(err) {\n        log.Printf(\"extractor chain cycle; misconfigured auth pipeline\")\n        return c.SendStatus(fiber.StatusInternalServerError)\n    }\n    return err\n}","preventionTips":["Build each Chain from leaf extractors only; never include the Chain itself.","Custom extractors that delegate should reference a specific inner extractor, not the enclosing Chain.","Add a unit test asserting Extract does not return ErrChainCycle on a representative request."],"tags":["extractors","chain","recursion","cycle","logic"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}