{"record":{"id":"27dcce441a8c5b66","repo":"caddyserver/caddy","slug":"directive-s-is-not-an-ordered-http-handler-so","errorCode":null,"errorMessage":"directive '%s' is not an ordered HTTP handler, so it cannot be used here - try placing within a route block or using the order global option","messagePattern":"directive '(.+?)' is not an ordered HTTP handler, so it cannot be used here - try placing within a route block or using the order global option","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/httpcaddyfile/httptype.go","lineNumber":1428,"sourceCode":"\t\t\t}\n\t\t}\n\t\tif len(route.MatcherSetsRaw) > 0 || len(route.HandlersRaw) > 0 {\n\t\t\trouteList = append(routeList, route)\n\t\t}\n\t} else {\n\t\trouteList = append(routeList, subroute.Routes...)\n\t}\n\n\treturn routeList\n}\n\n// buildSubroute turns the config values, which are expected to be routes\n// into a clean and orderly subroute that has all the routes within it.\nfunc buildSubroute(routes []ConfigValue, groupCounter counter, needsSorting bool) (*caddyhttp.Subroute, error) {\n\tif needsSorting {\n\t\tfor _, val := range routes {\n\t\t\tif !slices.Contains(directiveOrder, val.directive) {\n\t\t\t\treturn nil, fmt.Errorf(\"directive '%s' is not an ordered HTTP handler, so it cannot be used here - try placing within a route block or using the order global option\", val.directive)\n\t\t\t}\n\t\t}\n\n\t\tsortRoutes(routes)\n\t}\n\n\tsubroute := new(caddyhttp.Subroute)\n\n\t// some directives are mutually exclusive (only first matching\n\t// instance should be evaluated); this is done by putting their\n\t// routes in the same group\n\tmutuallyExclusiveDirs := map[string]*struct {\n\t\tcount     int\n\t\tgroupName string\n\t}{\n\t\t// as a special case, group rewrite directives so that they are mutually exclusive;\n\t\t// this means that only the first matching rewrite will be evaluated, and that's\n\t\t// probably a good thing, since there should never be a need to do more than one","sourceCodeStart":1410,"sourceCodeEnd":1446,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/httpcaddyfile/httptype.go#L1410-L1446","documentation":"buildSubroute was asked to sort a list of directives, but one directive in the list is not in the global `directive_order`. Sorting is only possible for known, ordered HTTP handlers, so the directive must appear inside an explicit `route` block (which preserves order) or be registered in the order global option.","triggerScenarios":"Using a directive inside a context where its relative order matters and it is not in the default order list — commonly a third-party plugin directive used at site level, or a directive placed where the adapter sorts (e.g. directly in a site block) when it has no default ordering. The fix paths are exactly the message: wrap it in a `route` block or add `order <directive> before|after <other>` to the global options.","commonSituations":"Installing a plugin (e.g. a custom handler) and using its directive without declaring order; new directives added in newer Caddy versions while an old `order` global overrides defaults; using `handle_path`-style grouping with unknown directives.","solutions":["Wrap the directive in a `route { }` block so no ordering is inferred","Add `order <directive> before <known>` (or after) to the global options block","If the directive comes from a plugin, confirm the plugin registers itself in directiveOrder or check its docs for the required order line","Verify spelling of the directive name — typos also surface here"],"exampleFix":"# before\nexample.com {\n  my_plugin_directive\n}\n# after\n{\n  order my_plugin_directive before respond\n}\nexample.com {\n  my_plugin_directive\n}","handlingStrategy":"validation","validationCode":"// Before adapting plugin configs, verify directive is orderable\nknown := directiveOrderSet()\nif !known[dir] && !insideRouteBlock(dir) {\n    return fmt.Errorf(\"declare order for %s or wrap in route\", dir)\n}","typeGuard":"func isOrderedDirective(d string) bool {\n    return slices.Contains(directiveOrder, d)\n}","tryCatchPattern":null,"preventionTips":["Wrap unknown/plugin directives in route blocks by default","Add `order <dir> before/after <x>` to global options once per plugin","Check plugin docs for the required order line at install time"],"tags":["caddy","caddyfile","directives","ordering","plugins"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}