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 directoryView on GitHub (pinned to a4447c1563)
Solutions
- Prefix the path with "static/" — e.g. ReadStaticFile("static/app.css")
- Remove leading slashes or other prefixes so the path is exactly 'static/<relative-path>'
- 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
- Build asset paths through a helper that enforces the static/ prefix
- Strip leading slashes before calling
- Only read static files after AppInit
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
- OpenStaticFile path must start with 'static/': %w
- path is a directory, not an image file
- failed to expand path: %w
- not a regular file: %s
- path is not a directory
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/d2502f240be07d9b.
Report an issue: GitHub.