{"record":{"id":"b04f5a6118d7ffae","repo":"fatedier/frp","slug":"failed-to-write-temp-file-w","errorCode":null,"errorMessage":"failed to write temp file: %w","messagePattern":"failed to write temp file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/source/store.go","lineNumber":158,"sourceCode":"\t\treturn fmt.Errorf(\"failed to marshal JSON: %w\", err)\n\t}\n\n\tdir := filepath.Dir(s.config.Path)\n\tif err := os.MkdirAll(dir, 0o755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create directory: %w\", err)\n\t}\n\n\ttmpPath := s.config.Path + \".tmp\"\n\n\tf, err := os.OpenFile(tmpPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create temp file: %w\", err)\n\t}\n\n\tif _, err := f.Write(data); err != nil {\n\t\tf.Close()\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"failed to write temp file: %w\", err)\n\t}\n\n\tif err := f.Sync(); err != nil {\n\t\tf.Close()\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"failed to sync temp file: %w\", err)\n\t}\n\n\tif err := f.Close(); err != nil {\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"failed to close temp file: %w\", err)\n\t}\n\n\tif err := os.Rename(tmpPath, s.config.Path); err != nil {\n\t\tos.Remove(tmpPath)\n\t\treturn fmt.Errorf(\"failed to rename temp file: %w\", err)\n\t}\n","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/source/store.go#L140-L176","documentation":"Writing the serialized JSON to the temp file failed after it was successfully created. The temp file is closed and removed, and the in-memory mutation is rolled back, so store state stays consistent — but the change is lost.","triggerScenarios":"ENOSPC (disk full) on the volume holding the store path; disk quota exceeded for the user; thin-provisioned volume out of space; I/O error from a failing disk (dmesg will show it).","commonSituations":"Long-running frpc on a small root disk or container overlay that filled up; CI environments with tiny tmpfs; k8s emptyDir with sizeLimit reached.","solutions":["Check free space: df -h <store-dir>","Free space or raise the quota/sizeLimit, or move the store to a volume with headroom","Inspect dmesg/SMART output if space is available but writes fail (failing disk)","Retry the failed operation (Add/Update/Remove) after space is restored — the rollback kept state consistent"],"exampleFix":"# before\n$ df -h /var/lib/frpc\nFilesystem  Size Used Avail Use% Mounted on\noverlay      10G  10G     0 100% /\n\n# after: free space, then retry the Add/Update call\n$ df -h /var/lib/frpc\noverlay      10G  8G  2G  80% /","handlingStrategy":"try-catch","validationCode":"func hasDiskSpace(path string, want uint64) bool {\n\tvar st syscall.Statfs_t\n\tif err := syscall.Statfs(filepath.Dir(path), &st); err != nil {\n\t\treturn true // unknown; let the write surface the real error\n\t}\n\treturn uint64(st.Bavail)*uint64(st.Bsize) > want\n}","typeGuard":null,"tryCatchPattern":"if err := store.AddProxy(cfg); err != nil {\n\tif errors.Is(err, syscall.ENOSPC) {\n\t\t// alert, free space, then retry the Add — rollback kept state consistent\n\t} else {\n\t\treturn err\n\t}\n}","preventionTips":["Monitor free space on the store volume and alert well before 0 bytes","Set k8s emptyDir sizeLimit or PVC requests sized for config churn","Treat 'failed to persist' as retriable only after the disk condition is fixed"],"tags":["filesystem","disk-full","io","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}