{"record":{"id":"167c7c481af8caa2","repo":"OpenNHP/opennhp","slug":"failed-to-create-file-v","errorCode":null,"errorMessage":"failed to create file: %v","messagePattern":"failed to create file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/db/utils.go","lineNumber":79,"sourceCode":"\n// Save saves the dataPrivateKeyBase64 to a file, the format of file name is data-<doId>.json\n// Notes: this default way to store data private key is not safe. In the wild environment, need to use a secure way to store data private key.\nfunc (d *DataPrivateKeyStore) Save(doId string) error {\n\t// Make sure the etc directory exists\n\tetcDir := \"etc/ztdo\"\n\tif err := os.MkdirAll(etcDir, 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create etc directory: %v\", err)\n\t}\n\n\tfileName := \"data-key-\" + doId + \".json\"\n\tfullPath := filepath.Join(common.ExeDirPath, etcDir, fileName)\n\tif _, err := os.Stat(fullPath); err == nil {\n\t\treturn fmt.Errorf(\"%v already exists, please delete it first\", fullPath)\n\t}\n\n\tfile, err := os.Create(fullPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create file: %v\", err)\n\t}\n\tdefer file.Close()\n\n\t_, err = file.Write(d.toJson())\n\treturn err\n}\n\nfunc (d *DataPrivateKeyStore) Delete(doId string) error {\n\tetcDir := \"etc/ztdo\"\n\tfileName := \"data-key-\" + doId + \".json\"\n\tfullPath := filepath.Join(common.ExeDirPath, etcDir, fileName)\n\n\t// delete the file\n\terr := os.Remove(fullPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete file: %v\", err)\n\t}\n\treturn nil","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/db/utils.go#L61-L97","documentation":"After passing the existence check, Save creates the key file with os.Create. If file creation fails - permission denied in the etc/ztdo directory, invalid doId producing an illegal filename, or disk full - this error is returned and no key material is persisted.","triggerScenarios":"Calling Save(doId) when os.Create(fullPath) fails: (1) the process cannot write to the (just-created or existing) etc/ztdo directory; (2) doId contains characters making the path invalid or too long on the OS; (3) no space / inode exhaustion on the volume.","commonSituations":"Directory owned by root while the daemon runs unprivileged; doId with slashes from a malformed identifier; full disk on a small container volume during key provisioning.","solutions":["Check the wrapped os error: for 'permission denied', fix ownership/permissions of <exeDir>/etc/ztdo so the process user can create files.","Validate doId before saving - it should be a UUID or safe identifier without path separators or illegal characters.","Check disk space and inodes (df / df -i) on the volume holding etc/ztdo.","Pre-create the directory with correct ownership, or run the daemon under an account with write access to its exe directory."],"exampleFix":"// before: unchecked doId can build an invalid path\nif err := store.Save(doId); err != nil { return err }\n// after: validate doId as a UUID first\nif _, err := uuid.Parse(doId); err != nil {\n\treturn fmt.Errorf(\"invalid doId %q: %w\", doId, err)\n}\nif err := store.Save(doId); err != nil { return err }","handlingStrategy":"validation","validationCode":"if _, err := uuid.Parse(doId); err != nil {\n\treturn fmt.Errorf(\"doId must be a UUID, got %q\", doId)\n}\nif err := syscall.Access(filepath.Join(common.ExeDirPath, \"etc/ztdo\"), syscall.W_OK); err != nil {\n\treturn fmt.Errorf(\"etc/ztdo not writable: %w\", err)\n}","typeGuard":"func validDoId(doId string) bool {\n\t_, err := uuid.Parse(doId)\n\treturn err == nil\n}","tryCatchPattern":"if err := store.Save(doId); err != nil {\n\tif errors.Is(errors.Unwrap(err), fs.ErrPermission) {\n\t\treturn fmt.Errorf(\"cannot create key file; check ownership of etc/ztdo: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Validate doId (UUID) before using it to build filenames.","Ensure the daemon user owns or can write to etc/ztdo.","Watch disk space/inodes on the state volume (df -h / df -i alerts).","Run Save once per doId as part of a single provisioning step."],"tags":["filesystem","file-write","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}