{"record":{"id":"104703cfc3bc8a66","repo":"wavetermdev/waveterm","slug":"failed-to-write-file-w-104703","errorCode":null,"errorMessage":"failed to write file: %w","messagePattern":"failed to write file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/waveappstore/waveappstore.go","lineNumber":283,"sourceCode":"\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\n\tfilePath, err := validateAndResolveFilePath(appDir, fileName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create directory: %w\", err)\n\t}\n\n\tif err := os.WriteFile(filePath, contents, 0644); err != nil {\n\t\treturn fmt.Errorf(\"failed to write file: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc ReadAppFile(appId string, fileName string) (*FileData, error) {\n\tif err := ValidateAppId(appId); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid appId: %w\", err)\n\t}\n\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","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/waveappstore/waveappstore.go#L265-L301","documentation":"WriteAppFile in pkg/waveappstore writes a file into the app's directory under ~/waveapps/<ns>/<app>. This error wraps the underlying os.WriteFile failure after the parent directory was already created successfully (waveappstore.go:282-284). It means the path resolved fine but the actual write syscall failed, and the wrapped OS error (permission denied, disk full, is-a-directory, etc.) is in the %w chain.","triggerScenarios":"Calling WriteAppFile (or the WriteAppFileCommand / WriteAppGoFileCommand RPCs) where os.WriteFile fails: target path exists as a directory, the file or parent dir is not writable, the filesystem is read-only or full, or an immutable/locked file is being overwritten.","commonSituations":"A directory named like the target file exists (e.g. someone created 'app.go/' by accident); ~/waveapps owned by another user after running the terminal with sudo; disk quota exceeded; editing an app while a sync/backup tool holds the file locked on Windows.","solutions":["Inspect the wrapped error with errors.Unwrap or %v on the returned error to get the exact errno (EACCES, ENOSPC, EISDIR).","If permission denied: check ownership/permissions of ~/waveapps/<ns>/<app>/ and chown/chmod back to the current user.","If the target path is a directory, remove or rename the conflicting directory.","If disk full, free space on the home filesystem and retry.","Ensure fileName is a relative path inside the app dir and the appId is valid so the resolved path is the intended one."],"exampleFix":"// before: blindly retrying a failed write\nerr := waveappstore.WriteAppFile(appId, \"app.go\", contents)\nif err != nil { return err } // opaque\n\n// after: surface and handle the wrapped OS error\nif err := waveappstore.WriteAppFile(appId, \"app.go\", contents); err != nil {\n    if errors.Is(err, fs.ErrPermission) {\n        return fmt.Errorf(\"cannot write %s: fix ownership of ~/waveapps\", appId)\n    }\n    if errors.Is(err, fs.ErrExist) {\n        return fmt.Errorf(\"a directory exists at the target path\")\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// Go: pre-check writability of the target before calling WriteAppFile\nfunc canWrite(appId, fileName string) error {\n    dir, err := waveappstore.GetAppDir(appId)\n    if err != nil { return err }\n    target := filepath.Join(dir, filepath.Clean(fileName))\n    if fi, err := os.Stat(target); err == nil && fi.IsDir() {\n        return fmt.Errorf(\"target is a directory: %s\", target)\n    }\n    f, err := os.OpenFile(filepath.Dir(target), os.O_WRONLY, 0)\n    if err != nil { return err }\n    f.Close()\n    return nil\n}","typeGuard":null,"tryCatchPattern":"err := waveappstore.WriteAppFile(appId, fileName, contents)\nif err != nil {\n    switch {\n    case errors.Is(err, fs.ErrPermission):\n        // fix ownership/permissions, prompt user\n    case errors.Is(err, fs.ErrExist):\n        // directory conflict at target path\n    default:\n        // surface wrapped errno\n    }\n}","preventionTips":["Never create directories whose names collide with file names you will write.","Run Wave as a normal user so ~/waveapps stays user-owned.","Check disk space before bulk writes.","Use errors.Is with fs error sentinels to branch on the real errno."],"tags":["filesystem","go","file-write","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}