larksuite/cli · error

%s field %s (%s): nil-capable field must declare nullable or

Error message

%s field %s (%s): nil-capable field must declare nullable or nonnullable

What it means

A field whose Go type can hold nil (pointer, slice, map, interface, etc.) must explicitly state whether null is an accepted/produced value via a `nullable` or `nonnullable` schema option. The compiler refuses to guess nil semantics, so the compiled schema is unambiguous for consumers.

Source

Thrown at shortcuts/common/typed_compile_data.go:241

		if previous, exists := seen[name]; exists {
			return typedObjectShape{}, fmt.Errorf("%s field %s JSON name %q duplicates field %s", path, field.Name, name, previous)
		}
		seen[name] = field.Name
		schema, err := parseSchemaTag(field.Tag.Get("schema"), field.Type, input)
		if err != nil {
			return typedObjectShape{}, fmt.Errorf("%s field %s (%s): %w", path, field.Name, name, err)
		}
		if !input && schema.defaultValue.Set {
			return typedObjectShape{}, fmt.Errorf("%s field %s (%s): Data field cannot declare default", path, field.Name, name)
		}
		if schema.required && omitempty {
			return typedObjectShape{}, fmt.Errorf("%s field %s (%s): required Data field cannot use omitempty", path, field.Name, name)
		}
		if schema.optional && !omitempty {
			return typedObjectShape{}, fmt.Errorf("%s field %s (%s): optional Data field must use omitempty", path, field.Name, name)
		}
		if isNilCapable(field.Type) && schema.nullable == nil {
			return typedObjectShape{}, fmt.Errorf("%s field %s (%s): nil-capable field must declare nullable or nonnullable", path, field.Name, name)
		}
		description := strings.TrimSpace(field.Tag.Get("doc"))
		if input && description == "" {
			return typedObjectShape{}, fmt.Errorf("%s field %s (%s): description is required via doc", path, field.Name, name)
		}
		fieldShape, err := shapeForType(field.Type, schema, input, active)
		if err != nil {
			return typedObjectShape{}, fmt.Errorf("%s field %s (%s): %w", path, field.Name, name, err)
		}
		shape.Fields = append(shape.Fields, typedValueField{Name: name, Description: description, Required: schema.required, Shape: fieldShape})
	}
	return shape, nil
}

func validateShape(shape typedValueShape, path string) error {
	if shape == nil {
		return fmt.Errorf("%s is nil", path)
	}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Add `nullable` to the schema tag if null is allowed, e.g. `schema:"optional,nullable"`
  2. Add `nonnullable` if nil is never valid (compiler/enforcement then guards that)
  3. Use a non-nil-capable type if the field can never be absent

Example fix

// before
Owner *string `json:"owner,omitempty" schema:"optional" doc:"..."`
// after
Owner *string `json:"owner,omitempty" schema:"optional,nullable" doc:"..."`
Defensive patterns

Strategy: validation

Validate before calling

if isNilCapable(f.Type) {
    s := f.Tag.Get("schema")
    if !strings.Contains(s, "nullable") && !strings.Contains(s, "nonnullable") {
        return fmt.Errorf("field %s: nil-capable type must declare nullable/nonnullable", f.Name)
    }
}

Type guard

func isNilCapable(t reflect.Type) bool {
    switch t.Kind() {
    case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface, reflect.Chan, reflect.Func:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Compiling a struct where e.g. `*string`, `[]Item`, or `map[string]string` field has no `nullable`/`nonnullable` option in its `schema:` tag, via compileStructShape.

Common situations: Introducing a pointer field for optionality without updating the schema tag; changing a value type to a pointer during refactor; maps/slices added for dynamic payloads.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/a6cfa1d58e442b5d. Report an issue: GitHub.