labstack/echo · error

static middleware failed to create sub-filesystem from confi

Error message

static middleware failed to create sub-filesystem from config.Root, error: %w

What it means

Returned by StaticConfig.ToMiddleware() (eager path, static.go:208) when fs.Sub(config.Filesystem, config.Root) fails while a custom Filesystem AND a non-'.' Root are both set. fs.Sub can fail if the root path is invalid for the filesystem implementation. This is detected at middleware construction time.

Source

Thrown at middleware/static.go:208

	}
	if config.DirectoryListTemplate == "" {
		config.DirectoryListTemplate = directoryListHTMLTemplate
	}

	dirListTemplate, tErr := template.New("index").Parse(config.DirectoryListTemplate)
	if tErr != nil {
		return nil, fmt.Errorf("echo static middleware directory list template parsing error: %w", tErr)
	}

	var once *sync.Once
	var fsErr error
	currentFS := config.Filesystem
	if config.Filesystem == nil {
		once = &sync.Once{}
	} else if config.Root != "." {
		tmpFs, fErr := fs.Sub(config.Filesystem, path.Join(".", config.Root))
		if fErr != nil {
			return nil, fmt.Errorf("static middleware failed to create sub-filesystem from config.Root, error: %w", fErr)
		}
		currentFS = tmpFs
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c *echo.Context) (err error) {
			if config.Skipper(c) {
				return next(c)
			}

			p := c.Request().URL.Path
			if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`.
				p = c.Param("*")
			}
			if config.EnablePathUnescaping {
				p, err = url.PathUnescape(p)
				if err != nil {
					return err

View on GitHub (pinned to 05489dc173)

Solutions

  1. Ensure config.Root matches a real directory inside the provided Filesystem (check embed directives vs Root).
  2. Use echo.MustSubFS(filesystem, root) which panics loudly during setup instead of failing per-request, or fs.Sub manually first to test.
  3. Set Root to '.' when your Filesystem is already scoped to the target directory.
  4. Double-check the embed path: '//go:embed static/*' embeds under 'static/', so Root should be 'static'.

Example fix

// before
//go:embed assets/*
var embedded embed.FS
cfg := middleware.StaticConfig{Filesystem: embedded, Root: "static"} // wrong: embedded under 'assets'
// after
cfg := middleware.StaticConfig{Filesystem: embedded, Root: "assets"}
// or scope it first:
sub, _ := fs.Sub(embedded, "assets")
cfg := middleware.StaticConfig{Filesystem: sub, Root: "."}
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the Root exists inside the filesystem before configuring Static.
func subExists(fsys fs.FS, root string) error {
    sub, err := fs.Sub(fsys, root)
    if err != nil { return err }
    entries, err := fs.ReadDir(sub, ".")
    if err != nil { return err }
    _ = entries
    return nil
}

Prevention

When it happens

Trigger: Providing StaticConfig.Filesystem (e.g. an embed.FS) together with a Root that does not exist within that filesystem, or a Root containing characters the FS rejects. For example Root='assets' when the embed.FS only contains 'static/'.

Common situations: Embedding files with '//go:embed static' but setting Root='assets'; using a custom fs.FS whose Sub implementation returns an error for the given path; mismatch between the directory embedded and the Root configured.

Related errors


AI-assisted analysis of labstack/echo@05489dc173 (2026-08-04). Data as JSON: /data/errors/3b3f1cff0238c578.json. Report an issue: GitHub.