{"record":{"id":"16c03b624f0b844a","repo":"AlistGo/alist","slug":"obj-is-not-local-object","errorCode":null,"errorMessage":"obj is not local Object","messagePattern":"obj is not local Object","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/mediatrack/driver.go","lineNumber":151,"sourceCode":"\nfunc (d *MediaTrack) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {\n\tdata := base.Json{\n\t\t\"parent_id\": dstDir.GetID(),\n\t\t\"ids\":       []string{srcObj.GetID()},\n\t}\n\turl := \"https://jayce.api.mediatrack.cn/v4/assets/batch/clone\"\n\t_, err := d.request(url, http.MethodPost, func(req *resty.Request) {\n\t\treq.SetBody(data)\n\t}, nil)\n\treturn err\n}\n\nfunc (d *MediaTrack) Remove(ctx context.Context, obj model.Obj) error {\n\tvar parentID string\n\tif o, ok := obj.(*Object); ok {\n\t\tparentID = o.ParentID\n\t} else {\n\t\treturn fmt.Errorf(\"obj is not local Object\")\n\t}\n\tdata := base.Json{\n\t\t\"origin_id\": parentID,\n\t\t\"ids\":       []string{obj.GetID()},\n\t}\n\turl := \"https://jayce.api.mediatrack.cn/v4/assets/batch/delete\"\n\t_, err := d.request(url, http.MethodDelete, func(req *resty.Request) {\n\t\treq.SetBody(data)\n\t}, nil)\n\treturn err\n}\n\nfunc (d *MediaTrack) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {\n\tsrc := \"assets/\" + uuid.New().String()\n\tvar resp UploadResp\n\t_, err := d.request(\"https://jayce.api.mediatrack.cn/v3/storage/tokens/asset\", http.MethodGet, func(req *resty.Request) {\n\t\treq.SetQueryParam(\"src\", src)\n\t}, &resp)","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/mediatrack/driver.go#L133-L169","documentation":"MediaTrack's Remove received a model.Obj that is not the driver-local *Object type, so it cannot read ParentID needed for the batch-delete payload. The driver requires objects it created itself (from its own List/Get); a foreign implementation of the model.Obj interface (e.g. a generic wrapper, mock, or object from another driver) fails this type assertion. The error message text is generic but the cause is strictly a type mismatch.","triggerScenarios":"Calling Remove with an object produced by a different driver or a test mock implementing model.Obj; passing a model.Object (value) instead of the driver's *Object pointer; framework paths that wrap or reconstruct objects losing the concrete type.","commonSituations":"Cross-driver operations in a multi-backend file manager; unit tests with hand-made fake objects; framework layers that serialize/deserialize objects between listing and removal, breaking the concrete type.","solutions":["Ensure the object passed to Remove came from this driver's List or Get (same *Object type)","If objects may arrive reconstructed, look the object up by ID via the driver's Get before calling Remove","In tests, construct the driver's real *Object rather than a mock","Refactor the driver to key removal off GetID() alone and resolve parentID server-side if the API permits"],"exampleFix":"// before\nif o, ok := obj.(*Object); ok {\n    parentID = o.ParentID\n} else {\n    return fmt.Errorf(\"obj is not local Object\")\n}\n\n// after\no, ok := obj.(*Object)\nif !ok {\n    fresh, err := d.Get(ctx, obj.GetPath()) // or resolve by ID\n    if err != nil {\n        return fmt.Errorf(\"obj is not local Object: %w\", err)\n    }\n    o, ok = fresh.(*Object)\n    if !ok {\n        return fmt.Errorf(\"obj is not local Object\")\n    }\n}\nparentID = o.ParentID","handlingStrategy":"type-guard","validationCode":"// Resolve to the driver's concrete object before Remove\nfresh, err := d.getObjByID(ctx, obj.GetID())\nif err != nil { return err }\nif _, ok := fresh.(*Object); !ok { return errors.New(\"driver returned unexpected object type\") }","typeGuard":"func isMediaTrackObject(o model.Obj) bool {\n    _, ok := o.(*Object)\n    return ok\n}","tryCatchPattern":"// Guard, then attempt re-resolution, then fail\nif !isMediaTrackObject(obj) {\n    if fresh, err := d.Get(ctx, obj.GetPath()); err == nil && isMediaTrackObject(fresh) {\n        obj = fresh\n    } else {\n        return fmt.Errorf(\"obj is not local Object (got %T)\", obj)\n    }\n}\nreturn d.Remove(ctx, obj)","preventionTips":["Pass only objects produced by the same driver instance to driver ops","In frameworks, re-resolve by path/ID before mutations","Make test fixtures use the driver's real Object type"],"tags":["mediatrack","type-assertion","go","driver"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}