{"id":"b0638c61ed4c9095","repo":"labstack/echo","slug":"static-middleware-failed-to-create-sub-filesystem-b0638c","errorCode":null,"errorMessage":"static middleware failed to create sub-filesystem: %w","messagePattern":"static middleware failed to create sub-filesystem: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/static.go","lineNumber":249,"sourceCode":"\t\t\t// 2. path.Clean() provides platform-independent behavior for URL paths\n\t\t\t// 3. The \"/\" prefix forces absolute path interpretation, removing \"..\" components\n\t\t\t// 4. Backslashes are treated as literal characters (not path separators), preventing traversal\n\t\t\t// See static_windows.go for Go 1.20+ filepath.Clean compatibility notes\n\t\t\tfilePath := path.Clean(\"./\" + p)\n\n\t\t\tif config.IgnoreBase {\n\t\t\t\troutePath := path.Base(strings.TrimRight(c.Path(), \"/*\"))\n\t\t\t\tbaseURLPath := path.Base(p)\n\t\t\t\tif baseURLPath == routePath {\n\t\t\t\t\ti := strings.LastIndex(filePath, routePath)\n\t\t\t\t\tfilePath = filePath[:i] + strings.Replace(filePath[i:], routePath, \"\", 1)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif once != nil {\n\t\t\t\tonce.Do(func() {\n\t\t\t\t\tif tmp, tmpErr := fs.Sub(c.Echo().Filesystem, config.Root); tmpErr != nil {\n\t\t\t\t\t\tfsErr = fmt.Errorf(\"static middleware failed to create sub-filesystem: %w\", tmpErr)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcurrentFS = tmp\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tif fsErr != nil {\n\t\t\t\t\treturn fsErr\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfile, err := currentFS.Open(filePath)\n\t\t\tif err != nil {\n\t\t\t\tif !isIgnorableOpenFileError(err) {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t// file with that path did not exist, so we continue down in middleware/handler chain, hoping that we end up in\n\t\t\t\t// handler that is meant to handle this request\n\t\t\t\terr = next(c)\n\t\t\t\tif err == nil {","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/labstack/echo/blob/05489dc1730161df26b72d1ae2a3ba6fb8178fc7/middleware/static.go#L231-L267","documentation":"Returned at request time (static.go:249, inside the middleware closure guarded by sync.Once) when fs.Sub(c.Echo().Filesystem, config.Root) fails for the DEFAULT filesystem path (config.Filesystem was nil). Because no custom filesystem was provided, Echo lazily creates the sub-filesystem on the first request. The once.Do ensures it only attempts once; on failure every subsequent request returns fsErr.","triggerScenarios":"Using Static middleware (or StaticWithConfig) with the default OS filesystem and a Root directory that does not exist relative to the working directory. The error surfaces on the first request that hits the static handler.","commonSituations":"Root points to a directory that isn't present at runtime (typo, wrong relative path, deployed without assets); running the binary from a different working directory than expected; Root cleaned to a path the OS fs.FS rejects.","solutions":["Verify config.Root exists relative to the process working directory (the default filesystem is os.DirFS('.')).","Use an absolute path or run the binary from the directory containing the assets.","Prefer embedding assets with embed.FS + StaticConfig.Filesystem for predictable, portable paths.","Test the path with os.Stat or fs.Sub(e.Filesystem, root) during setup."],"exampleFix":"// before\ne.Use(middleware.StaticWithConfig(middleware.StaticConfig{Root: \"./public\"})) // dir missing at runtime\n// after: embed for portability\n//go:embed public\nvar publicFS embed.FS\nsub, _ := fs.Sub(publicFS, \"public\")\ne.Use(middleware.StaticWithConfig(middleware.StaticConfig{Filesystem: sub, Root: \".\"}))","handlingStrategy":"validation","validationCode":"// For the default OS filesystem, verify Root resolves before serving.\nfunc rootExists(root string) error {\n    info, err := os.Stat(root)\n    if err != nil { return err }\n    if !info.IsDir() { return fmt.Errorf(\"%s is not a directory\", root) }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer embed.FS for portable, immutable assets.","Run the binary from a known working directory or use absolute paths for Root.","Call echo.MustSubFS or fs.Sub at startup to fail fast rather than on first request."],"tags":["static","filesystem","runtime","config"],"analyzedSha":"05489dc1730161df26b72d1ae2a3ba6fb8178fc7","analyzedAt":"2026-08-04T21:32:47.783Z","schemaVersion":2}