{"record":{"id":"483c268050120c37","repo":"AlistGo/alist","slug":"delete-offline-tasks-failed-s","errorCode":null,"errorMessage":"delete offline tasks failed: %s","messagePattern":"delete offline tasks failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/guangyapan/offline.go","lineNumber":129,"sourceCode":"\treturn resp.Data.List, nil\n}\n\nfunc (d *GuangYaPan) DeleteOfflineTasks(ctx context.Context, taskIDs []string, deleteFiles bool) error {\n\tif err := d.ensureAccessToken(ctx); err != nil {\n\t\treturn err\n\t}\n\tif len(taskIDs) == 0 {\n\t\treturn nil\n\t}\n\n\tvar resp offlineDeleteResp\n\tif err := d.postAPI(ctx, \"/cloudcollection/v2/delete_task\", map[string]any{\n\t\t\"taskIds\": taskIDs,\n\t}, &resp); err != nil {\n\t\treturn err\n\t}\n\tif !isSuccessMsg(resp.Msg) {\n\t\treturn fmt.Errorf(\"delete offline tasks failed: %s\", strings.TrimSpace(resp.Msg))\n\t}\n\treturn nil\n}\n\nfunc (d OfflineResolveData) defaultName(fileURL string) string {\n\tif d.BTResInfo != nil && strings.TrimSpace(d.BTResInfo.FileName) != \"\" {\n\t\treturn strings.TrimSpace(d.BTResInfo.FileName)\n\t}\n\tu, err := url.Parse(fileURL)\n\tif err == nil {\n\t\tname := strings.TrimSpace(stdpath.Base(u.Path))\n\t\tif name != \"\" && name != \".\" && name != \"/\" {\n\t\t\tif decoded, err := url.PathUnescape(name); err == nil {\n\t\t\t\tname = decoded\n\t\t\t}\n\t\t\treturn name\n\t\t}\n\t}","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/guangyapan/offline.go#L111-L147","documentation":"Thrown by GuangYaPan's DeleteOfflineTasks after POST /cloudcollection/v2/delete_task succeeds at the HTTP layer but returns a non-success Msg. The server processed (or rejected) the batch deletion of task IDs; the trimmed server message is included. Note deletion is all-or-nothing from the caller's view — partial server-side success is not distinguishable here.","triggerScenarios":"Passing task IDs that were already deleted, expired, or belong to another account; sending a batch larger than the v2 endpoint accepts; token revoked between the listing call and the delete call.","commonSituations":"UI deletes a task twice (double-click, stale list refresh); concurrent deletion from another client between list and delete; retrying a delete that actually succeeded (the first attempt's response was lost).","solutions":["Inspect resp.Msg for the server's reason (already-deleted vs. auth vs. batch-size errors).","Re-list tasks (ListOfflineTasks) and filter out IDs that no longer exist before retrying the delete.","Treat 'already deleted/not found' style messages as success (idempotent delete) at the caller level if the workflow allows.","Split very large taskIDs batches into smaller chunks if the msg indicates a limit."],"exampleFix":"// before\nif err := d.DeleteOfflineTasks(ctx, taskIDs, true); err != nil { return err }\n\n// after\nif err := d.DeleteOfflineTasks(ctx, taskIDs, true); err != nil {\n    if strings.Contains(err.Error(), \"not exist\") { // server-specific idempotency marker\n        return nil // already gone; treat as success\n    }\n    return err\n}","handlingStrategy":"validation","validationCode":"// prune IDs against a fresh listing before deleting\nlist, err := d.ListOfflineTasks(ctx, \"\", 100)\nif err != nil { return err }\nlive := make(map[string]bool)\nfor _, t := range list { live[t.TaskID] = true }\nvalid := taskIDs[:0]\nfor _, id := range taskIDs { if live[id] { valid = append(valid, id) } }\nif len(valid) == 0 { return nil }","typeGuard":null,"tryCatchPattern":"if err := d.DeleteOfflineTasks(ctx, ids, del); err != nil {\n    if strings.Contains(err.Error(), \"delete offline tasks failed\") && isNotFoundMsg(err) {\n        return nil // idempotent\n    }\n    return err\n}","preventionTips":["Re-list before delete to drop already-removed IDs","Guard against double-submit in UIs","Batch deletes within provider limits"],"tags":["guangyapan","offline-download","delete","idempotency"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}