{"record":{"id":"911980dc3eba4ad4","repo":"wavetermdev/waveterm","slug":"failed-to-read-file-w-911980","errorCode":null,"errorMessage":"failed to read file: %w","messagePattern":"failed to read file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/waveappstore/waveappstore.go","lineNumber":311,"sourceCode":"\n\tappDir, err := GetAppDir(appId)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilePath, err := validateAndResolveFilePath(appDir, fileName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfileInfo, err := os.Stat(filePath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to stat file: %w\", err)\n\t}\n\n\tcontents, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read file: %w\", err)\n\t}\n\n\treturn &FileData{\n\t\tContents: contents,\n\t\tModTs:    fileInfo.ModTime().UnixMilli(),\n\t}, nil\n}\n\nfunc DeleteAppFile(appId string, fileName string) error {\n\tif err := ValidateAppId(appId); err != nil {\n\t\treturn fmt.Errorf(\"invalid appId: %w\", err)\n\t}\n\n\tappDir, err := GetAppDir(appId)\n\tif err != nil {\n\t\treturn err\n\t}\n","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/waveappstore/waveappstore.go#L293-L329","documentation":"After os.Stat succeeds, ReadAppFile reads the file contents with os.ReadFile and wraps any failure (waveappstore.go:309-312). This is rarer than the stat error — the file existed at stat time but could not be read, usually due to a permissions change, the file being deleted in between (race), or an I/O error.","triggerScenarios":"Calling ReadAppFile when the file's read permission was revoked between Stat and ReadFile, the file was removed by another process in that window, or the underlying storage reports an I/O error (failing disk, network mount dropped).","commonSituations":"Another Wave instance or external tool deleting/rotating files while you read; running as a user without read permission on files created by a different uid; home directory on a flaky NFS/network share.","solutions":["Unwrap and check errors.Is(err, fs.ErrPermission) — fix file ownership/permissions with chmod/chown.","Retry once; if the cause was a TOCTOU race (file deleted between stat and read), a retry will surface the cleaner not-exist condition.","Check dmesg/mount health if the wrapped error is an I/O error (EIO) and the home dir is on a network filesystem.","Avoid concurrent delete+read on the same app file from multiple processes."],"exampleFix":"// before\ndata, err := waveappstore.ReadAppFile(appId, fileName)\nif err != nil { return err }\n\n// after\ndata, err := waveappstore.ReadAppFile(appId, fileName)\nif err != nil {\n    if errors.Is(err, fs.ErrPermission) {\n        return fmt.Errorf(\"no read permission on %s in app %s\", fileName, appId)\n    }\n    return err\n}","handlingStrategy":"retry","validationCode":"// ensure read permission before reading\nfunc readable(appId, fileName string) error {\n    dir, err := waveappstore.GetAppDir(appId)\n    if err != nil { return err }\n    f, err := os.Open(filepath.Join(dir, filepath.Clean(fileName)))\n    if err != nil { return err }\n    return f.Close()\n}","typeGuard":null,"tryCatchPattern":"var data *waveappstore.FileData\nvar err error\nfor i := 0; i < 2; i++ { // one retry covers stat/read race\n    data, err = waveappstore.ReadAppFile(appId, fileName)\n    if err == nil || errors.Is(err, fs.ErrNotExist) {\n        break\n    }\n}","preventionTips":["Avoid deleting/rotating app files while other processes may read them.","Keep app file permissions consistent (0644, owned by the running user).","Watch for home dirs on unreliable network mounts.","Distinguish permission vs race vs I/O errors with errors.Is."],"tags":["filesystem","go","file-read","permissions"],"backgroundTag":"file-read-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}