wavetermdev/waveterm · error

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

Error message

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

What it means

ReadStaticFile serves embedded/app static assets through the default Tsunami client's StaticFS. Paths must be relative to the static root, expressed with a literal 'static/' prefix; anything else is rejected by wrapping fs.ErrNotExist so callers see a normal not-exist error.

Source

Thrown at tsunami/app/defaultclient.go:222

	client.DeclareSecret(secretName, secretDesc, secretOptional)
	return os.Getenv(secretName)
}

func PrintAppManifest() {
	client := engine.GetDefaultClient()
	client.PrintAppManifest()
}

// ReadStaticFile reads a file from the embedded static filesystem.
// The path MUST start with "static/" (e.g., "static/config.json").
// Returns the file contents or an error if the file doesn't exist or can't be read.
func ReadStaticFile(path string) ([]byte, 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("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

View on GitHub (pinned to a4447c1563)

Solutions

  1. Prefix the path with "static/" — e.g. ReadStaticFile("static/app.css")
  2. Remove leading slashes or other prefixes so the path is exactly 'static/<relative-path>'
  3. If StaticFS is nil, move the call after AppInit instead of relying on path fixes

Example fix

// before
data, err := tsunami.ReadStaticFile("/assets/logo.svg")
// after
data, err := tsunami.ReadStaticFile("static/assets/logo.svg")
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

data, err := tsunami.ReadStaticFile(path)
if err != nil {
	if errors.Is(err, fs.ErrNotExist) {
		return fmt.Errorf("static asset %q missing or bad prefix", path)
	}
	return err
}

Prevention

When it happens

Trigger: Calling tsunami ReadStaticFile("foo.css"), "/static/foo.css" (leading slash), or any path before AppInit has populated client.StaticFS (which yields the related 'static files not available' error).

Common situations: Forgetting the static/ prefix after refactoring asset paths; using absolute filesystem-style paths; calling during early init before the app client is initialized.

Related errors


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