{"record":{"id":"91564ae284c09e36","repo":"wavetermdev/waveterm","slug":"openstaticfile-path-must-start-with-static-w","errorCode":null,"errorMessage":"OpenStaticFile path must start with 'static/': %w","messagePattern":"OpenStaticFile path must start with 'static/': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/app/defaultclient.go","lineNumber":238,"sourceCode":"\t}\n\tif !strings.HasPrefix(path, \"static/\") {\n\t\treturn nil, fmt.Errorf(\"ReadStaticFile path must start with 'static/': %w\", fs.ErrNotExist)\n\t}\n\t// Strip \"static/\" prefix since the FS is already sub'd to the static directory\n\trelativePath := strings.TrimPrefix(path, \"static/\")\n\treturn fs.ReadFile(client.StaticFS, relativePath)\n}\n\n// OpenStaticFile opens a file from the embedded static filesystem.\n// The path MUST start with \"static/\" (e.g., \"static/config.json\").\n// Returns an fs.File or an error if the file doesn't exist or can't be opened.\nfunc OpenStaticFile(path string) (fs.File, error) {\n\tclient := engine.GetDefaultClient()\n\tif client.StaticFS == nil {\n\t\treturn nil, errors.New(\"static files not available before app initialization; use AppInit to access files during initialization\")\n\t}\n\tif !strings.HasPrefix(path, \"static/\") {\n\t\treturn nil, fmt.Errorf(\"OpenStaticFile path must start with 'static/': %w\", fs.ErrNotExist)\n\t}\n\t// Strip \"static/\" prefix since the FS is already sub'd to the static directory\n\trelativePath := strings.TrimPrefix(path, \"static/\")\n\treturn client.StaticFS.Open(relativePath)\n}\n\n// ListStaticFiles returns FileInfo for all files in the embedded static filesystem.\n// The Name() of each FileInfo will be the full path prefixed with \"static/\" (e.g., \"static/config.json\"),\n// which can be passed directly to ReadStaticFile or OpenStaticFile.\nfunc ListStaticFiles() ([]fs.FileInfo, error) {\n\tclient := engine.GetDefaultClient()\n\tif client.StaticFS == nil {\n\t\treturn nil, errors.New(\"static files not available before app initialization; use AppInit to access files during initialization\")\n\t}\n\n\tvar fileInfos []fs.FileInfo\n\terr := fs.WalkDir(client.StaticFS, \".\", func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil {","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/app/defaultclient.go#L220-L256","documentation":"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.","triggerScenarios":"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.","commonSituations":"Passing URLs or filesystem paths instead of FS-relative asset paths; streaming files during early startup hooks; renamed asset directories breaking assumed prefixes.","solutions":["Ensure the path starts with \"static/\" followed by the FS-relative asset path","Strip leading '/' or extra base directories from the path before calling","Defer opening static files until after AppInit so StaticFS is populated"],"exampleFix":"// before\nf, err := tsunami.OpenStaticFile(\"index.html\")\n// after\nf, err := tsunami.OpenStaticFile(\"static/index.html\")","handlingStrategy":"validation","validationCode":"if !strings.HasPrefix(path, \"static/\") {\n\treturn fmt.Errorf(\"OpenStaticFile requires static/ prefix, got %q\", path)\n}","typeGuard":null,"tryCatchPattern":"f, err := tsunami.OpenStaticFile(path)\nif err != nil {\n\tif errors.Is(err, fs.ErrNotExist) {\n\t\treturn nil, fmt.Errorf(\"static file %q unavailable\", path)\n\t}\n\treturn nil, err\n}\ndefer f.Close()","preventionTips":["Normalize paths through a single helper that adds the static/ prefix","Avoid opening static files in pre-init hooks","Handle fs.ErrNotExist gracefully since it is wrapped into the error"],"tags":["filesystem","static-assets","validation"],"backgroundTag":"invalid-static-path","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}