{"record":{"id":"44e43f50a0369fc6","repo":"AlistGo/alist","slug":"failed-to-get-original-hash-w","errorCode":null,"errorMessage":"failed to get original hash: %w","messagePattern":"failed to get original hash: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/proton_drive/util.go","lineNumber":604,"sourceCode":"\n\tparentLinkID := srcLink.ParentLinkID\n\tif parentLinkID == \"\" {\n\t\treturn nil, fmt.Errorf(\"cannot rename root folder\")\n\t}\n\n\tencryptedName, err := d.encryptFileName(ctx, newName, parentLinkID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to encrypt filename: %w\", err)\n\t}\n\n\tnewHash, err := d.generateFileNameHash(ctx, newName, parentLinkID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to generate new hash: %w\", err)\n\t}\n\n\toriginalHash, err := d.getOriginalNameHash(srcLink)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get original hash: %w\", err)\n\t}\n\n\trenameReq := RenameRequest{\n\t\tName:               encryptedName,\n\t\tNameSignatureEmail: d.MainShare.Creator,\n\t\tHash:               newHash,\n\t\tOriginalHash:       originalHash,\n\t}\n\n\terr = d.executeRenameAPI(ctx, srcLink.LinkID, renameReq)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"rename API call failed: %w\", err)\n\t}\n\n\treturn &model.Object{\n\t\tName:     newName,\n\t\tSize:     srcObj.GetSize(),\n\t\tModified: srcObj.ModTime(),","sourceCodeStart":586,"sourceCodeEnd":622,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/proton_drive/util.go#L586-L622","documentation":"Thrown inside the rename flow when d.getOriginalNameHash(srcLink) fails. That helper (util.go:415-425) only fails in two cases: srcLink is nil, or srcLink.Hash is an empty string. Proton Drive requires the link's current name hash in the rename request (OriginalHash), so the operation aborts before the HTTP call.","triggerScenarios":"Calling Rename on a link object whose Hash field was never populated: a link returned by searchByPath/list without hash metadata, a link struct built locally instead of fetched via d.getLink, or a very old Proton item created before name hashing existed.","commonSituations":"Stale or partially-populated link cache after the item was touched by another client; hand-constructed proton.Link objects; renaming items shared by an account whose keyring/hash key could not be decrypted, leaving Hash empty in the listing.","solutions":["Fetch a fresh link right before renaming: srcLink, err = d.getLink(ctx, srcLink.LinkID) and retry","If the fresh link still has an empty Hash, recompute it with d.generateNameHash(ctx, currentName, srcLink.ParentLinkID) from the decrypted current name","Reject nil srcLink early in the rename entry point with a clear 'source link not found' error"],"exampleFix":"// before\noriginalHash, err := d.getOriginalNameHash(srcLink)\n// after\nfreshLink, err := d.getLink(ctx, srcLink.LinkID)\nif err != nil { return nil, fmt.Errorf(\"failed to refresh link: %w\", err) }\noriginalHash, err := d.getOriginalNameHash(freshLink)","handlingStrategy":"validation","validationCode":"// before rename:\nlink, err := d.getLink(ctx, srcLink.LinkID)\nif err != nil { return err }\nif link == nil || link.Hash == \"\" {\n    return fmt.Errorf(\"source link %s has no name hash; refusing rename\", srcLink.LinkID)\n}","typeGuard":"func hasNameHash(l *proton.Link) bool { return l != nil && l.Hash != \"\" }","tryCatchPattern":"if err := obj.Rename(ctx, obj, newName); err != nil {\n    if strings.Contains(err.Error(), \"failed to get original hash\") {\n        // refresh listing then retry once\n    }\n}","preventionTips":["Always fetch link metadata via getLink before rename/move, never trust cached link structs","Refresh the directory listing after any external client has touched the drive"],"tags":["proton-drive","rename","crypto","validation"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}