valyala/fasthttp · error

cannot open file %q: %w

Error message

cannot open file %q: %w

What it means

openIndexFile tries to open a pre-compressed or cached index file (e.g. index.html.gz) before falling back to the real index. If opening that index file fails for a reason other than fs.ErrNotExist (e.g. EACCES, EISDIR), the error is wrapped and returned rather than silently falling back.

Source

Thrown at fs.go:1552

func (h *fsHandler) openIndexFile(ctx *RequestCtx, dirPath string, mustCompress bool, fileEncoding string) (*fsFile, error) {
	for _, indexName := range h.indexNames {
		indexFilePath := indexName
		if dirPath != "" {
			indexFilePath = dirPath + "/" + indexName
		}

		ff, err := h.openFSFile(indexFilePath, mustCompress, fileEncoding)
		if err == nil {
			return ff, nil
		}
		if mustCompress && err == errNoCreatePermission {
			ctx.Logger().Printf("insufficient permissions for saving compressed file for %q. Serving uncompressed file. "+
				"Allow write access to the directory with this file in order to improve fasthttp performance", indexFilePath)
			mustCompress = false
			return h.openFSFile(indexFilePath, mustCompress, fileEncoding)
		}
		if !errors.Is(err, fs.ErrNotExist) {
			return nil, fmt.Errorf("cannot open file %q: %w", indexFilePath, err)
		}
	}

	if !h.generateIndexPages {
		return nil, fmt.Errorf("cannot access directory without index page: directory %q", dirPath)
	}

	return h.createDirIndex(ctx, dirPath, mustCompress, fileEncoding)
}

var (
	errDirIndexRequired   = errors.New("directory index required")
	errNoCreatePermission = errors.New("no 'create file' permissions")
)

func (h *fsHandler) createDirIndex(ctx *RequestCtx, dirPath string, mustCompress bool, fileEncoding string) (*fsFile, error) {
	w := &bytebufferpool.ByteBuffer{}

View on GitHub (pinned to c96f600972)

Solutions

  1. Log the wrapped inner error to see the real errno (EACCES vs EISDIR)
  2. Fix permissions: chmod/chown index.html so the fasthttp process can read it
  3. Ensure the path is a regular file, not a directory: ls -la the directory and rename/remove the conflicting entry
  4. If you intentionally don't want index files, disable index page generation (GenerateIndexPages: false) so a clearer error is returned

Example fix

// before (server config with unreadable index)
fs.New(fs.Config{Root: "./static"}) // index.html 0600 root-owned
// after
// chmod 644 ./static/index.html && chown www-data ./static/index.html
fs.New(fs.Config{Root: "./static", GenerateIndexPages: true})
Defensive patterns

Strategy: fallback

Validate before calling

if fi, err := os.Stat(indexPath); err != nil || !fi.Mode().IsRegular() || fi.Mode().Perm()&0o400 == 0 {
    log.Printf("index file %s unreadable", indexPath)
}

Type guard

func readableRegularFile(path string) bool {
    fi, err := os.Stat(path)
    return err == nil && fi.Mode().IsRegular()
}

Try / catch

if err := serveIndex(ctx, dir); err != nil {
    if !errors.Is(err, fs.ErrNotExist) {
        log.Printf("index open failed: %v", err) // real errno is wrapped
    }
    http.Error(ctx, "not found", 404)
}

Prevention

When it happens

Trigger: Directory request handled by fasthttp FS/FileServer where the index file path exists but cannot be opened: wrong permissions on index.html, index path is actually a directory, or an I/O error; also compressed-file paths with unreadable ACLs.

Common situations: Deploy leaves index.html owned by root with 0600 while the server runs as another user; a file literally named like the index path but being a directory; NFS/overlayfs transient I/O errors in containers.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/363f04367656839d. Report an issue: GitHub.