{"record":{"id":"65ced7660ace3a1b","repo":"juicedata/juicefs","slug":"delete-src-failed","errorCode":null,"errorMessage":"delete src failed","messagePattern":"delete src failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/meta/sql.go","lineNumber":2527,"sourceCode":"\t\t\t*tInode = dino\n\t\t\tm.parseAttr(&dn, tAttr)\n\t\t}\n\n\t\tif exchange {\n\t\t\tif _, err := s.Cols(\"inode\", \"type\").Update(&de, &edge{Parent: parentSrc, Name: se.Name}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err := s.Cols(\"inode\", \"type\").Update(&se, &edge{Parent: parentDst, Name: de.Name}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err := s.Cols(\"ctime\", \"ctimensec\", \"parent\").Update(dn, &node{Inode: dino}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif n, err := s.Delete(&edge{Parent: parentSrc, Name: se.Name}); err != nil {\n\t\t\t\treturn err\n\t\t\t} else if n != 1 {\n\t\t\t\treturn fmt.Errorf(\"delete src failed\")\n\t\t\t}\n\t\t\tif dino > 0 {\n\t\t\t\tif trash > 0 {\n\t\t\t\t\tnewSpace, newInode = align4K(0), 1\n\t\t\t\t\tif de.Type == TypeFile {\n\t\t\t\t\t\tnewSpace = align4K(dn.Length)\n\t\t\t\t\t}\n\t\t\t\t\tif _, err := s.Cols(\"ctime\", \"ctimensec\", \"parent\").Update(dn, &node{Inode: dino}); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tname := m.trashEntry(parentDst, dino, string(de.Name))\n\t\t\t\t\tif err = mustInsert(s, &edge{Parent: trash, Name: []byte(name), Inode: dino, Type: de.Type}); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t} else if de.Type != TypeDirectory && dn.Nlink > 0 {\n\t\t\t\t\tif _, err := s.Cols(\"ctime\", \"ctimensec\", \"nlink\", \"parent\").Update(dn, &node{Inode: dino}); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}","sourceCodeStart":2509,"sourceCodeEnd":2545,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/meta/sql.go#L2509-L2545","documentation":"Thrown inside dbMeta.Rename's transaction when deleting the source `edge` row (the directory entry being renamed) deletes 0 or more than 1 rows instead of exactly 1. Just before this, the code re-read the edge within the same transaction, so a count != 1 means the entry vanished or is inconsistent between the read and the delete — an internal consistency guard, not a user-facing SQL error.","triggerScenarios":"A concurrent rename/unlink of the same source name racing between the in-transaction edge re-read and the DELETE; metadata row manipulation outside JuiceFS; SQL engines with weak isolation (READ UNCOMMITTED / non-transactional DDL interference).","commonSituations":"Multiple clients renaming/deleting the same file path simultaneously; a data-recovery tool or manual DELETE touching jfs_edge mid-rename; running the volume on a database with the transaction isolation lowered from the default.","solutions":["Retry the operation — a lost race with a concurrent delete/rename is typically transient; the transaction rolls back safely.","Check whether another process is deleting/renaming the same path concurrently and serialize at the application level if needed.","Run `juicefs fsck <meta-url>` to verify metadata consistency if the error repeats on a quiet volume.","Confirm the database's transaction isolation is at the engine default (REPEATABLE READ/READ COMMITTED)."],"exampleFix":"// before: two clients rename/unlink src simultaneously -> one gets \"delete src failed\"\n// after: handle the race by retrying the failed rename\nerr := m.Rename(ctx, parentSrc, nameSrc, parentDst, nameDst)\nif err != nil && strings.Contains(err.Error(), \"delete src failed\") {\n    err = m.Rename(ctx, parentSrc, nameSrc, parentDst, nameDst) // re-check ENOENT path first\n}","handlingStrategy":"retry","validationCode":"// check the source entry still exists before renaming\nif _, err := os.Stat(srcPath); os.IsNotExist(err) {\n    return nil // nothing to rename; skip instead of racing\n}","typeGuard":null,"tryCatchPattern":"err := fs.Rename(ctx, src, dst)\nif err != nil && strings.Contains(err.Error(), \"delete src failed\") {\n    if _, statErr := fs.Stat(src); statErr != nil {\n        return nil // src really vanished — concurrent delete won the race\n    }\n    err = fs.Rename(ctx, src, dst) // transient race; retry once\n}","preventionTips":["Avoid multiple clients renaming/unlinking the same path concurrently.","Never manipulate jfs_edge rows outside JuiceFS tooling.","Leave DB transaction isolation at the engine default."],"tags":["metadata","rename","race-condition","database"],"backgroundTag":"internal-invariant-violation","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}