larksuite/cli · error

Args must be a non-pointer struct, got %s

Error message

Args must be a non-pointer struct, got %s

What it means

compileInput validates the Args struct type of a typed shortcut input definition via reflection. It throws this error when the reflected Args type's Kind() is not reflect.Struct — e.g. a pointer to struct (*MyArgs), a map, or a slice was passed. The typed-input machinery requires a concrete non-pointer struct so it can enumerate fields with tags.

Source

Thrown at shortcuts/common/typed_compile_args.go:26

	"encoding/json"
	"fmt"
	"math"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

var (
	flagNamePattern  = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`)
	aliasNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]*$`)
)

const extensionCommandPkgPath = "github.com/larksuite/cli/extension/command"

func compileInput(argsType reflect.Type, definition typedInputDefinition) ([]compiledInputField, map[string]int, error) {
	if argsType.Kind() != reflect.Struct {
		return nil, nil, fmt.Errorf("Args must be a non-pointer struct, got %s", argsType)
	}
	supplements := make(map[string]typedInputField, len(definition.Fields))
	for i, supplement := range definition.Fields {
		if !flagNamePattern.MatchString(supplement.Name) {
			return nil, nil, fmt.Errorf("Input.Fields[%d].Name %q is not a canonical flag name", i, supplement.Name)
		}
		if _, exists := supplements[supplement.Name]; exists {
			return nil, nil, fmt.Errorf("Input.Fields contains duplicate flag %q", supplement.Name)
		}
		supplements[supplement.Name] = supplement
	}
	var fields []compiledInputField
	seenGo := make(map[string]struct{})
	if err := collectArgFields(argsType, nil, false, &fields, seenGo, supplements); err != nil {
		return nil, nil, err
	}
	fieldByName := make(map[string]int, len(fields))
	allNames := make(map[string]string, len(fields))

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Change the Args type from a pointer (*T) to the plain struct value type T.
  2. If the args are dynamic, use the loose/untyped input path instead of the typed compile path.
  3. If Args is an alias to a non-struct type, define a real struct with flag/arg tags.

Example fix

// before
var def = typedInputDefinition{ Args: reflect.TypeOf(&CreateTaskArgs{}) }
// after
var def = typedInputDefinition{ Args: reflect.TypeOf(CreateTaskArgs{}) }
Defensive patterns

Strategy: validation

Validate before calling

func validateArgsType(t reflect.Type) error {
  if t == nil { return errors.New("Args type is nil") }
  if t.Kind() == reflect.Pointer { t = t.Elem() }
  if t.Kind() != reflect.Struct {
    return fmt.Errorf("Args must be a non-pointer struct, got %s", t)
  }
  return nil
}

Type guard

func isStructValue(v any) bool {
  t := reflect.TypeOf(v)
  return t != nil && t.Kind() == reflect.Struct
}

Prevention

When it happens

Trigger: Passing *MyArgs (pointer-to-struct), a map, slice, or interface type as the Args value of a typedInputDefinition, or registering a shortcut whose args generic parameter resolves to a non-struct kind.

Common situations: Declaring shortcuts with a pointer receiver-style Args type like &Config{} by habit; copying a definition that used a map[string]any for loose input; refactoring that changed a struct parameter to a pointer for 'optional' semantics.

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 larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/312999b62f5cf7f9. Report an issue: GitHub.