{"record":{"id":"a4e5670a1ea9d03c","repo":"netbirdio/netbird","slug":"failed-to-create-file","errorCode":null,"errorMessage":"failed to create file","messagePattern":"failed to create file","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"upload-server/server/local.go","lineNumber":141,"sourceCode":"\t\thttp.Error(w, \"invalid path\", http.StatusBadRequest)\n\t\tlog.Warnf(\"Path traversal attempt blocked (file): %s\", filePath)\n\t\treturn\n\t}\n\n\tif err = os.MkdirAll(dirPath, 0750); err != nil {\n\t\thttp.Error(w, \"failed to create upload dir\", http.StatusInternalServerError)\n\t\tlog.Errorf(\"Failed to create upload dir: %v\", err)\n\t\treturn\n\t}\n\n\tflags := os.O_WRONLY | os.O_CREATE | os.O_EXCL\n\tf, err := os.OpenFile(filePath, flags, 0600)\n\tif err != nil {\n\t\tif os.IsExist(err) {\n\t\t\thttp.Error(w, \"file already exists\", http.StatusConflict)\n\t\t\treturn\n\t\t}\n\t\thttp.Error(w, \"failed to create file\", http.StatusInternalServerError)\n\t\tlog.Errorf(\"Failed to create file %s: %v\", filePath, err)\n\t\treturn\n\t}\n\tdefer func() { _ = f.Close() }()\n\n\tif _, err = f.Write(body); err != nil {\n\t\thttp.Error(w, \"failed to write file\", http.StatusInternalServerError)\n\t\tlog.Errorf(\"Failed to write file %s: %v\", filePath, err)\n\t\treturn\n\t}\n\n\tlog.Infof(\"Uploaded file %s\", filePath)\n\tw.WriteHeader(http.StatusOK)\n}\n","sourceCodeStart":123,"sourceCodeEnd":156,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/upload-server/server/local.go#L123-L156","documentation":"HTTP 500 returned when os.OpenFile(filePath, O_WRONLY|O_CREATE|O_EXCL, 0600) fails with an error other than EEXIST, after the upload directory was created successfully. The underlying errno is logged as 'Failed to create file <path>' on the server.","triggerScenarios":"Directory permissions (0750, owner-only) deny create to the process user; the final path component is invalid (contains a decoded '/', a NUL byte, or exceeds NAME_MAX 255); a directory already exists at the exact file path; SELinux/AppArmor denial on the state dir.","commonSituations":"File names taken straight from client uploads containing separators or extreme length; hardened container security profiles blocking writes outside allowed labels; upload subdirectories created by a different user.","solutions":["Read the server log line 'Failed to create file <path>: <err>' and fix per errno: permission denied -> ownership/umask; file name too long -> shorten; is a directory -> rename","Restrict client file names to a safe single segment (letters, digits, '-', '_', '.') before upload","On SELinux/AppArmor systems, verify the process label may write under STORE_DIR (check audit logs)"],"exampleFix":"// before: raw client filename used as segment\nname := fileHeader.Filename // \"../../x\" or 300 chars -> 500\n\n// after: sanitize to one safe segment\nname := filepath.Base(fileHeader.Filename)\nname = strings.Map(func(r rune) rune {\n\tif unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '.' { return r }\n\treturn '_'\n}, name)","handlingStrategy":"try-catch","validationCode":"// client-side: keep the file segment short and separator-free\nname := filepath.Base(fileName)\nif len(name) > 128 || strings.ContainsAny(name, \"/\\\\\\x00\") {\n\tname = uuid.NewString()\n}","typeGuard":null,"tryCatchPattern":"On 500 'failed to create file', read the server log for the errno (permission denied, name too long, is a directory), fix the environment or the name, then retry once with a fresh URL from GET /upload-url.","preventionTips":["Cap client file names to a conservative length (<128 chars) and a safe charset","Ensure the service user owns every level under STORE_DIR","On SELinux/AppArmor hosts, allow writes to the state dir label in the policy"],"tags":["filesystem","permissions","upload","go"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}