{"record":{"id":"1fd74737f1bc8ef4","repo":"OpenNHP/opennhp","slug":"v-already-exists-please-delete-it-first","errorCode":null,"errorMessage":"%v already exists, please delete it first","messagePattern":"(.+?) already exists, please delete it first","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/db/utils.go","lineNumber":74,"sourceCode":"func (d *DataPrivateKeyStore) Generate(mode ztdolib.DataKeyPairECCMode) (privateKey []byte) {\n\tecdh := core.NewECDH(mode.ToEccType())\n\td.DataPrivateKeyBase64 = ecdh.PrivateKeyBase64()\n\treturn ecdh.PrivateKey()\n}\n\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","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/db/utils.go#L56-L92","documentation":"Save refuses to overwrite an existing data private key file. Before creating data-key-<doId>.json it runs os.Stat; if the file already exists it returns this error instructing the caller to delete it first. This is an intentional safety guard against silently replacing a ZTDO data private key, which would make previously wrapped data undecryptable.","triggerScenarios":"Calling Save(doId) when data-key-<doId>.json already exists in <exeDir>/etc/ztdo: (1) re-generating a key for a doId that already has one; (2) a leftover file from a previous run with the same doId; (3) calling Save twice in one flow.","commonSituations":"Re-running an init/registration step on an already-provisioned daemon; a doId collision (same UUID reused); operator retries a bootstrap script without cleaning up the first attempt's key file.","solutions":["If the existing key is still valid, load it with NewDataPrivateKeyStoreWith(doId) instead of generating a new one.","If the key must be rotated, explicitly call Delete(doId) (or remove the file) first, understanding that data wrapped with the old key becomes undecryptable.","Before saving, check existence with os.Stat on <exeDir>/etc/ztdo/data-key-<doId>.json and branch your logic accordingly.","Fix duplicate doId generation if the same identifier is being reused across runs."],"exampleFix":"// before: blind Save fails on second run\nif err := store.Save(doId); err != nil { return err }\n// after: only save when no key exists yet\npath := filepath.Join(common.ExeDirPath, \"etc/ztdo\", \"data-key-\"+doId+\".json\")\nif _, err := os.Stat(path); os.IsNotExist(err) {\n\tif err := store.Save(doId); err != nil { return err }\n}","handlingStrategy":"validation","validationCode":"path := filepath.Join(common.ExeDirPath, \"etc/ztdo\", \"data-key-\"+doId+\".json\")\nexists := false\nif _, err := os.Stat(path); err == nil { exists = true }\nif exists {\n\t// load existing key instead of saving a new one\n}","typeGuard":"func keyAlreadySaved(doId string) bool {\n\t_, err := os.Stat(filepath.Join(common.ExeDirPath, \"etc/ztdo\", \"data-key-\"+doId+\".json\"))\n\treturn err == nil\n}","tryCatchPattern":"err := store.Save(doId)\nif err != nil && strings.Contains(err.Error(), \"already exists\") {\n\tstore, err = db.NewDataPrivateKeyStoreWith(doId) // reuse existing key\n}","preventionTips":["Make bootstrap scripts idempotent: load an existing key before generating a new one.","Never reuse doIds across provisioning runs; always generate fresh UUIDs.","If rotation is intended, call Delete(doId) explicitly and acknowledge old wrapped data becomes undecryptable.","Document that Save is intentionally non-overwriting."],"tags":["filesystem","duplicate","key-management"],"backgroundTag":"file-already-exists","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"}