AlistGo/alist · error

obj is not local Object

Error message

obj is not local Object

What it means

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.

Source

Thrown at drivers/mediatrack/driver.go:151

func (d *MediaTrack) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
	data := base.Json{
		"parent_id": dstDir.GetID(),
		"ids":       []string{srcObj.GetID()},
	}
	url := "https://jayce.api.mediatrack.cn/v4/assets/batch/clone"
	_, err := d.request(url, http.MethodPost, func(req *resty.Request) {
		req.SetBody(data)
	}, nil)
	return err
}

func (d *MediaTrack) Remove(ctx context.Context, obj model.Obj) error {
	var parentID string
	if o, ok := obj.(*Object); ok {
		parentID = o.ParentID
	} else {
		return fmt.Errorf("obj is not local Object")
	}
	data := base.Json{
		"origin_id": parentID,
		"ids":       []string{obj.GetID()},
	}
	url := "https://jayce.api.mediatrack.cn/v4/assets/batch/delete"
	_, err := d.request(url, http.MethodDelete, func(req *resty.Request) {
		req.SetBody(data)
	}, nil)
	return err
}

func (d *MediaTrack) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
	src := "assets/" + uuid.New().String()
	var resp UploadResp
	_, err := d.request("https://jayce.api.mediatrack.cn/v3/storage/tokens/asset", http.MethodGet, func(req *resty.Request) {
		req.SetQueryParam("src", src)
	}, &resp)

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Ensure the object passed to Remove came from this driver's List or Get (same *Object type)
  2. If objects may arrive reconstructed, look the object up by ID via the driver's Get before calling Remove
  3. In tests, construct the driver's real *Object rather than a mock
  4. Refactor the driver to key removal off GetID() alone and resolve parentID server-side if the API permits

Example fix

// before
if o, ok := obj.(*Object); ok {
    parentID = o.ParentID
} else {
    return fmt.Errorf("obj is not local Object")
}

// after
o, ok := obj.(*Object)
if !ok {
    fresh, err := d.Get(ctx, obj.GetPath()) // or resolve by ID
    if err != nil {
        return fmt.Errorf("obj is not local Object: %w", err)
    }
    o, ok = fresh.(*Object)
    if !ok {
        return fmt.Errorf("obj is not local Object")
    }
}
parentID = o.ParentID
Defensive patterns

Strategy: type-guard

Validate before calling

// Resolve to the driver's concrete object before Remove
fresh, err := d.getObjByID(ctx, obj.GetID())
if err != nil { return err }
if _, ok := fresh.(*Object); !ok { return errors.New("driver returned unexpected object type") }

Type guard

func isMediaTrackObject(o model.Obj) bool {
    _, ok := o.(*Object)
    return ok
}

Try / catch

// Guard, then attempt re-resolution, then fail
if !isMediaTrackObject(obj) {
    if fresh, err := d.Get(ctx, obj.GetPath()); err == nil && isMediaTrackObject(fresh) {
        obj = fresh
    } else {
        return fmt.Errorf("obj is not local Object (got %T)", obj)
    }
}
return d.Remove(ctx, obj)

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/16c03b624f0b844a. Report an issue: GitHub.