{"record":{"id":"f5a19adf08354ba5","repo":"caddyserver/caddy","slug":"root-file-system-not-specified","errorCode":null,"errorMessage":"root file system not specified","messagePattern":"root file system not specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/templates/tplcontext.go","lineNumber":152,"sourceCode":"// trusted files. If it is not trusted, be sure to use escaping functions\n// in your template.\nfunc (c TemplateContext) funcReadFile(filename string) (string, error) {\n\tbodyBuf := bufPool.Get().(*bytes.Buffer)\n\tbodyBuf.Reset()\n\tdefer bufPool.Put(bodyBuf)\n\n\terr := c.readFileToBuffer(filename, bodyBuf)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn bodyBuf.String(), nil\n}\n\n// readFileToBuffer reads a file into a buffer\nfunc (c TemplateContext) readFileToBuffer(filename string, bodyBuf *bytes.Buffer) error {\n\tif c.Root == nil {\n\t\treturn fmt.Errorf(\"root file system not specified\")\n\t}\n\n\tfile, err := c.Root.Open(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\t_, err = io.Copy(bodyBuf, file)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// funcHTTPInclude returns the body of a virtual (lightweight) request\n// to the given URI on the same server. Note that included bodies","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/templates/tplcontext.go#L134-L170","documentation":"Returned by TemplateContext.readFileToBuffer (backing the 'include' template function) when c.Root is nil, i.e. the template context has no root file system to resolve relative paths against. The templates handler normally sets Root from FileRoot, which defaults to {http.vars.root}.","triggerScenarios":"Using {{ include \"file\" }} (or any function that reads files, like 'stat') in a context where the root variable is unset: no file_server/root directive set the {http.vars.root} placeholder and FileRoot was not configured on the templates handler.","commonSituations":"A route with templates but no root directive (common when templates are used for pure string responses without a file server), a root defined on a different route that does not apply to this request, or FileRoot pointing to a variable that is never populated.","solutions":["Add a root directive (Caddyfile) or set root on the enclosing route so {http.vars.root} resolves","Or set the templates handler's own file_root (Caddyfile: templates { file_root /path })","Verify the placeholder expands by logging {http.vars.root} in an access log or respond directive","Avoid include/file functions if the deployment has no file root at all"],"exampleFix":"# before\nlocalhost {\n    templates\n    respond `{{ include \"partial.html\" }}`\n}\n\n# after\nlocalhost {\n    root * /srv/site\n    templates\n    respond `{{ include \"partial.html\" }}`\n}","handlingStrategy":"validation","validationCode":"# Caddyfile: ensure root applies before templates\nroot * /srv\ntemplates\n\n# Debug: confirm the placeholder resolves\nrespond \"root={http.vars.root}\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair templates-with-file-functions and a root directive in the same route","Or set templates { file_root } explicitly to decouple from route vars","Smoke-test pages that use include after any route restructuring"],"tags":["caddy","templates","filesystem","configuration"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}