labstack/echo · error

file does not implement io.ReadSeeker

Error message

file does not implement io.ReadSeeker

What it means

Returned by fsFile (used by Context.File, FileFS, Inline, Attachment) when the file opened from the configured fs.FS does not implement io.ReadSeeker. http.ServeContent requires a ReadSeeker to set Content-Length, handle Range requests, and stream efficiently; a reader that only implements Read cannot be served this way. Standard os.File and embed.FS files implement ReadSeeker, but custom fs.FS implementations may not.

Source

Thrown at context.go:695

		return ErrNotFound
	}
	defer f.Close()

	fi, _ := f.Stat()
	if fi.IsDir() {
		file = filepath.ToSlash(filepath.Join(file, indexPage)) // ToSlash is necessary for Windows. fs.Open and os.Open are different in that aspect.
		f, err = filesystem.Open(file)
		if err != nil {
			return ErrNotFound
		}
		defer f.Close()
		if fi, err = f.Stat(); err != nil {
			return err
		}
	}
	ff, ok := f.(io.ReadSeeker)
	if !ok {
		return errors.New("file does not implement io.ReadSeeker")
	}
	http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), ff)
	return nil
}

// Attachment sends a response as attachment, prompting client to save the file.
//
// Avoid using the leading `/` slash as most of the Go standard library fs.FS implementations require relative paths for
// file operations.
func (c *Context) Attachment(file, name string) error {
	return c.contentDisposition(file, name, "attachment")
}

// Inline sends a response as inline, opening the file in the browser.
//
// Avoid using the leading `/` slash as most of the Go standard library fs.FS implementations require relative paths for
// file operations.
func (c *Context) Inline(file, name string) error {

View on GitHub (pinned to 05489dc173)

Solutions

  1. Ensure your fs.FS file objects implement both Read and Seek (i.e. io.ReadSeeker); wrap a bytes.Reader or use os.File
  2. If the data is fully in memory, open into a bytes.Reader which implements io.ReadSeeker
  3. If seeking is impossible, read the content yourself and send it via c.Blob or c.Stream instead of c.File/FileFS

Example fix

// before — custom file only implements Read
type myFile struct{ r io.Reader }
func (f *myFile) Read(p []byte) (int, error) { return f.r.Read(p) }

// after — implement Seek as well
func (f *myFile) Seek(off int64, whence int) (int64, error) {
    // delegate to underlying seeker or implement in-memory seek
}
Defensive patterns

Strategy: type-guard

Type guard

// Verify your fs.FS returns files implementing io.ReadSeeker
func fsImplementsReadSeeker(fsys fs.FS) bool {
    // best-effort: open a known file and type-assert
    f, err := fsys.Open(".")
    if err != nil { return true } // can't check, assume ok
    defer f.Close()
    _, ok := f.(io.ReadSeeker)
    return ok
}

Try / catch

if err := c.FileFS("path", myFS); err != nil {
    if strings.Contains(err.Error(), "io.ReadSeeker") {
        // fall back to reading content manually
        data, _ := fs.ReadFile(myFS, "path")
        return c.Blob(http.StatusOK, mime.TypeByExtension(".html"), data)
    }
    return err
}

Prevention

When it happens

Trigger: Providing a custom fs.FS whose Open returns an io.Reader that does not also implement Seek (io.ReadSeeker), then calling c.FileFS("path", myFS) or setting Echo.Filesystem and calling c.File("path").

Common situations: Custom virtual filesystem reading from a network source, compressed archive, or stream. Wrapping an io.Reader in a struct that only exposes Read. Testing with a mock FS that returns a bytes.Reader incorrectly wrapped.

Related errors


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