{"record":{"id":"7ec4aa12e156a4c9","repo":"caddyserver/caddy","slug":"v-is-not-a-directory","errorCode":null,"errorMessage":"%v is not a directory","messagePattern":"(.+?) is not a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/templates/tplcontext.go","lineNumber":409,"sourceCode":"// funcListFiles reads and returns a slice of names from the given\n// directory relative to the root of c.\nfunc (c TemplateContext) funcListFiles(name string) ([]string, error) {\n\tif c.Root == nil {\n\t\treturn nil, fmt.Errorf(\"root file system not specified\")\n\t}\n\n\tdir, err := c.Root.Open(path.Clean(name))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer dir.Close()\n\n\tstat, err := dir.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !stat.IsDir() {\n\t\treturn nil, fmt.Errorf(\"%v is not a directory\", name)\n\t}\n\n\tdirInfo, err := dir.Readdir(0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnames := make([]string, len(dirInfo))\n\tfor i, fileInfo := range dirInfo {\n\t\tnames[i] = fileInfo.Name()\n\t}\n\n\treturn names, nil\n}\n\n// funcFileExists returns true if filename can be opened successfully.\nfunc (c TemplateContext) funcFileExists(filename string) (bool, error) {\n\tif c.Root == nil {","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/templates/tplcontext.go#L391-L427","documentation":"Returned by the templates module's listFiles function when the path given exists and was opened successfully but is not a directory (stat.IsDir() is false). The function then cannot Readdir it.","triggerScenarios":"{{ listFiles \"notes.txt\" }} where notes.txt is a regular file; or a dynamic path built from a placeholder that resolves to a file name instead of a folder.","commonSituations":"Passing a file path where a directory was intended, using '.' on a root that maps to a single file, or template logic like {{ listFiles .Path }} where .Path points at an article file rather than its containing directory.","solutions":["Point listFiles at a directory, e.g. {{ listFiles \"/posts\" }} or use path.Dir of the file","Guard with {{ if (stat .).IsDir }} style checks or split files vs dirs before calling","Log or render the offending path temporarily to see what the placeholder resolves to","Trim trailing filenames from computed paths in the template"],"exampleFix":"<!-- before -->\n{{ listFiles \"/posts/2024/post.md\" }}\n\n<!-- after -->\n{{ listFiles \"/posts/2024\" }}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"{{ $s := stat \"/posts/2024\" }}{{ if $s.IsDir }}{{ listFiles \"/posts/2024\" }}{{ else }}not a directory{{ end }}","tryCatchPattern":null,"preventionTips":["Always stat-check before listFiles when the path is dynamic","Derive directories with path.Dir rather than trusting raw variables","Render candidate paths during development to verify they point at folders"],"tags":["caddy","templates","filesystem","path"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}