{"record":{"id":"a8dded4abc90365e","repo":"OpenNHP/opennhp","slug":"failed-to-delete-file-v","errorCode":null,"errorMessage":"failed to delete file: %v","messagePattern":"failed to delete file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"endpoints/db/utils.go","lineNumber":95,"sourceCode":"\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\n}\n\nfunc (d *DataPrivateKeyStore) toJson() []byte {\n\tdataPrkStoreJson, err := json.Marshal(d)\n\tif err != nil {\n\t\treturn []byte(\"{}\")\n\t} else {\n\t\treturn dataPrkStoreJson\n\t}\n}\n\nfunc (d *DataPrivateKeyStore) fromJson(jsonData []byte) error {\n\terr := json.Unmarshal(jsonData, d)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"json parsing error: %s\", err)\n\t}","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/db/utils.go#L77-L113","documentation":"DataPrivateKeyStore.Delete removes data-key-<doId>.json from <exeDir>/etc/ztdo via os.Remove. If removal fails - most commonly because the file does not exist, or the directory is not writable - this wrapped error is returned. Note there is no ENOENT special-casing: deleting a key that was never saved also produces this error.","triggerScenarios":"Calling Delete(doId) when: (1) no key file exists for that doId (never saved, already deleted, or mistyped doId); (2) the process lacks write permission on etc/ztdo; (3) the file is locked/EBUSY on some platforms or sits on a read-only filesystem.","commonSituations":"Idempotent teardown scripts calling Delete unconditionally; cleanup path racing with another process that already deleted the key; running as a different user than the one who created the files.","solutions":["Check the wrapped os error: if it is ENOENT ('no such file or directory'), treat the delete as already done and ignore, or use errors.Is(err, fs.ErrNotExist).","Verify the exact path <exeDir>/etc/ztdo/data-key-<doId>.json and the doId spelling before deleting.","Grant the process user write permission on etc/ztdo if the error is 'permission denied'.","Guard concurrent deletes with a lock or check os.Stat before calling Delete."],"exampleFix":"// before: Delete fails on missing file\nif err := store.Delete(doId); err != nil { return err }\n// after: tolerate already-deleted keys\nif err := store.Delete(doId); err != nil && !errors.Is(err, fs.ErrNotExist) {\n\treturn err\n}","handlingStrategy":"try-catch","validationCode":"path := filepath.Join(common.ExeDirPath, \"etc/ztdo\", \"data-key-\"+doId+\".json\")\nif _, err := os.Stat(path); os.IsNotExist(err) {\n\treturn nil // nothing to delete\n}","typeGuard":"func isNotExistWrapped(err error) bool {\n\treturn errors.Is(err, fs.ErrNotExist)\n}","tryCatchPattern":"err := store.Delete(doId)\nif err != nil && !errors.Is(err, fs.ErrNotExist) {\n\treturn fmt.Errorf(\"deleting data key %s: %w\", doId, err)\n}","preventionTips":["Treat ENOENT as success in cleanup/teardown paths to make deletes idempotent.","Serialize key-file operations with a lock to avoid delete/delete races.","Confirm the doId before deleting - the filename is derived from it.","Run cleanup under the same user that created the files to avoid permission surprises."],"tags":["filesystem","file-delete","idempotency"],"backgroundTag":"file-not-found","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"}