wavetermdev/waveterm · error

OpenStaticFile path must start with 'static/': %w

Error message

OpenStaticFile path must start with 'static/': %w

What it means

OpenStaticFile opens a handle to an embedded static asset via the default client's StaticFS. Like ReadStaticFile it requires a 'static/'-prefixed relative path; other forms are rejected with this error wrapping fs.ErrNotExist, and calls before AppInit fail because StaticFS is nil.

Source

Thrown at tsunami/app/defaultclient.go:238

	}
	if !strings.HasPrefix(path, "static/") {
		return nil, fmt.Errorf("ReadStaticFile path must start with 'static/': %w", fs.ErrNotExist)
	}
	// Strip "static/" prefix since the FS is already sub'd to the static directory
	relativePath := strings.TrimPrefix(path, "static/")
	return fs.ReadFile(client.StaticFS, relativePath)
}

// OpenStaticFile opens a file from the embedded static filesystem.
// The path MUST start with "static/" (e.g., "static/config.json").
// Returns an fs.File or an error if the file doesn't exist or can't be opened.
func OpenStaticFile(path string) (fs.File, error) {
	client := engine.GetDefaultClient()
	if client.StaticFS == nil {
		return nil, errors.New("static files not available before app initialization; use AppInit to access files during initialization")
	}
	if !strings.HasPrefix(path, "static/") {
		return nil, fmt.Errorf("OpenStaticFile path must start with 'static/': %w", fs.ErrNotExist)
	}
	// Strip "static/" prefix since the FS is already sub'd to the static directory
	relativePath := strings.TrimPrefix(path, "static/")
	return client.StaticFS.Open(relativePath)
}

// ListStaticFiles returns FileInfo for all files in the embedded static filesystem.
// The Name() of each FileInfo will be the full path prefixed with "static/" (e.g., "static/config.json"),
// which can be passed directly to ReadStaticFile or OpenStaticFile.
func ListStaticFiles() ([]fs.FileInfo, error) {
	client := engine.GetDefaultClient()
	if client.StaticFS == nil {
		return nil, errors.New("static files not available before app initialization; use AppInit to access files during initialization")
	}

	var fileInfos []fs.FileInfo
	err := fs.WalkDir(client.StaticFS, ".", func(path string, d fs.DirEntry, err error) error {
		if err != nil {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Ensure the path starts with "static/" followed by the FS-relative asset path
  2. Strip leading '/' or extra base directories from the path before calling
  3. Defer opening static files until after AppInit so StaticFS is populated

Example fix

// before
f, err := tsunami.OpenStaticFile("index.html")
// after
f, err := tsunami.OpenStaticFile("static/index.html")
Defensive patterns

Strategy: validation

Validate before calling

if !strings.HasPrefix(path, "static/") {
	return fmt.Errorf("OpenStaticFile requires static/ prefix, got %q", path)
}

Try / catch

f, err := tsunami.OpenStaticFile(path)
if err != nil {
	if errors.Is(err, fs.ErrNotExist) {
		return nil, fmt.Errorf("static file %q unavailable", path)
	}
	return nil, err
}
defer f.Close()

Prevention

When it happens

Trigger: Calling tsunami OpenStaticFile with a path missing the 'static/' prefix, using an absolute path like '/static/x', or invoking it before app initialization when client.StaticFS is still nil.

Common situations: Passing URLs or filesystem paths instead of FS-relative asset paths; streaming files during early startup hooks; renamed asset directories breaking assumed prefixes.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/91564ae284c09e36. Report an issue: GitHub.