micro/go-micro · error

model: result must be a pointer to a slice

Error message

model: result must be a pointer to a slice

What it means

List unmarshals query results by reflecting into the caller's result parameter, which must be a pointer to a slice (typically *[]*T) so it can be grown and populated. Any other shape — a struct pointer, a map, a nil pointer, or a non-pointer slice — is rejected with this error before any query runs.

Source

Thrown at model/memory.go:147

		return err
	}

	m.mu.Lock()
	defer m.mu.Unlock()

	tbl := m.tables[schema.Table]
	if _, ok := tbl[key]; !ok {
		return ErrNotFound
	}
	delete(tbl, key)
	return nil
}

func (m *memoryModel) List(ctx context.Context, result interface{}, opts ...QueryOption) error {
	// result must be *[]*T
	rv := reflect.ValueOf(result)
	if rv.Kind() != reflect.Pointer || rv.Elem().Kind() != reflect.Slice {
		return fmt.Errorf("model: result must be a pointer to a slice")
	}
	sliceVal := rv.Elem()
	elemType := sliceVal.Type().Elem() // *T
	structType := elemType
	if structType.Kind() == reflect.Pointer {
		structType = structType.Elem()
	}

	m.mu.RLock()
	s, ok := m.types[structType]
	m.mu.RUnlock()
	if !ok {
		return ErrNotRegistered
	}

	q := ApplyQueryOptions(opts...)

	m.mu.RLock()

View on GitHub (pinned to 24529f1404)

Solutions

  1. Pass a pointer to a slice: declare var users []*User and call List(ctx, &users).
  2. Ensure the slice's element type matches the model struct (or pointer to it).
  3. Check that you're not accidentally passing the slice by value — add the & operator.
  4. Initialize the variable rather than passing a typed nil interface.

Example fix

// before
users := []*User{}
err := store.List(ctx, users) // slice, not pointer

// after
var users []*User
err := store.List(ctx, &users)
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure result is *[]*T before calling
func isPtrToSlice(result interface{}) bool {
    rv := reflect.ValueOf(result)
    return rv.Kind() == reflect.Pointer && rv.Elem().Kind() == reflect.Slice
}

Type guard

func assertListResult[T any](result *[]*T) bool { return result != nil }
// or generic helper:
func listOf[T any]() *[]*T { return new([]*T) }
users := listOf[User]()
err := store.List(ctx, users)

Try / catch

var users []*User
if err := store.List(ctx, &users); err != nil {
    if strings.Contains(err.Error(), "pointer to a slice") {
        return fmt.Errorf("caller bug: List needs &users (*[]*User): %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling memoryModel.List(ctx, result) with result of type []T, *T, *[]T (element not pointer is fine — the check is pointer-to-slice — but *map, struct, or nil fail), or forgetting the & when passing a slice variable.

Common situations: Copy-pasting the Read call pattern (which takes a *T) for List, refactoring List's signature and forgetting the address-of operator, or generics misuse where the result parameter's type was changed.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/b8a7c933adfeaacf. Report an issue: GitHub.