{"record":{"id":"586155fa2f916684","repo":"fatedier/frp","slug":"failed-to-create-temp-file-w","errorCode":null,"errorMessage":"failed to create temp file: %w","messagePattern":"failed to create temp file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/source/store.go","lineNumber":152,"sourceCode":"\tfor _, v := range s.visitors {\n\t\tstored.Visitors = append(stored.Visitors, v1.TypedVisitorConfig{VisitorConfigurer: v})\n\t}\n\n\tdata, err := jsonx.MarshalIndent(stored, \"\", \"  \")\n\tif err != nil {\n\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}","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/source/store.go#L134-L170","documentation":"os.OpenFile on Path + \".tmp\" (O_WRONLY|O_CREATE|O_TRUNC, mode 0600) failed, so the atomic-write sequence could not even start. Everything after this (write, fsync, rename) is skipped; the in-memory change that triggered the save has already been applied and will be rolled back by persistOrRollbackUnlocked.","triggerScenarios":"Directory not writable for the process user; read-only filesystem mount; an existing directory named exactly <Path>.tmp blocks file creation; SELinux/AppArmor denial; path length over NAME_MAX because of the .tmp suffix.","commonSituations":"Container with a read-only or root-owned volume mounted at the store directory; a leftover <store>.tmp directory from an earlier interrupted run or backup tool; security policy blocking writes outside allowed labels.","solutions":["Confirm write permission on the directory: touch <dir>/.wtest as the running user","If a DIRECTORY named <Path>.tmp exists, remove or rename it","For read-only mounts, remount rw or relocate Path to a writable volume","Check audit logs (ausearch -m avc / dmesg) for SELinux/AppArmor denials if permissions look fine"],"exampleFix":"# before: .tmp path occupied by a directory\nls -la /var/lib/frpc/\n# store.json.tmp/  <- directory, OpenFile fails\n\n# after\nrm -rf /var/lib/frpc/store.json.tmp","handlingStrategy":"validation","validationCode":"func canCreateTempFile(path string) error {\n\ttmp := path + \".tmp\"\n\tif fi, err := os.Stat(tmp); err == nil && fi.IsDir() {\n\t\treturn fmt.Errorf(\"%s exists and is a directory\", tmp)\n\t}\n\tf, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot write %s: %w\", tmp, err)\n\t}\n\tf.Close()\n\tos.Remove(tmp)\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check at startup that the process can write the store directory (probe-create a file)","Avoid read-only mounts for the store path; use emptyDir/PVC in k8s","Clean up stray *.tmp artifacts in the store directory during deploy"],"tags":["filesystem","permissions","read-only","temp-file","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}