{"record":{"id":"d2502f240be07d9b","repo":"wavetermdev/waveterm","slug":"readstaticfile-path-must-start-with-static-w","errorCode":null,"errorMessage":"ReadStaticFile path must start with 'static/': %w","messagePattern":"ReadStaticFile path must start with 'static/': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsunami/app/defaultclient.go","lineNumber":222,"sourceCode":"\tclient.DeclareSecret(secretName, secretDesc, secretOptional)\n\treturn os.Getenv(secretName)\n}\n\nfunc PrintAppManifest() {\n\tclient := engine.GetDefaultClient()\n\tclient.PrintAppManifest()\n}\n\n// ReadStaticFile reads a file from the embedded static filesystem.\n// The path MUST start with \"static/\" (e.g., \"static/config.json\").\n// Returns the file contents or an error if the file doesn't exist or can't be read.\nfunc ReadStaticFile(path string) ([]byte, 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(\"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","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/app/defaultclient.go#L204-L240","documentation":"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.","triggerScenarios":"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).","commonSituations":"Forgetting the static/ prefix after refactoring asset paths; using absolute filesystem-style paths; calling during early init before the app client is initialized.","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"],"exampleFix":"// before\ndata, err := tsunami.ReadStaticFile(\"/assets/logo.svg\")\n// after\ndata, err := tsunami.ReadStaticFile(\"static/assets/logo.svg\")","handlingStrategy":"validation","validationCode":"if !strings.HasPrefix(path, \"static/\") {\n\treturn fmt.Errorf(\"path %q needs a static/ prefix\", path)\n}","typeGuard":null,"tryCatchPattern":"data, err := tsunami.ReadStaticFile(path)\nif err != nil {\n\tif errors.Is(err, fs.ErrNotExist) {\n\t\treturn fmt.Errorf(\"static asset %q missing or bad prefix\", path)\n\t}\n\treturn err\n}","preventionTips":["Build asset paths through a helper that enforces the static/ prefix","Strip leading slashes before calling","Only read static files after AppInit"],"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"}