larksuite/cli · error

custom JSON type %s requires an explicit Shape

Error message

custom JSON type %s requires an explicit Shape

What it means

Struct fields whose types implement custom JSON encoding (json.Marshaler or encoding.TextMarshaler, e.g. time.Time, json.Number-style wrappers, big.Int) are rejected: the compiler cannot infer the wire schema from a MarshalJSON method, since arbitrary bytes could be emitted. An explicit Shape or a plain struct is required.

Source

Thrown at shortcuts/common/typed_compile_data.go:154

	case reflect.Slice, reflect.Array:
		if baseType == jsonRawMessageType {
			return nil, fmt.Errorf("json.RawMessage requires an explicit Shape")
		}
		if baseType.Elem().Kind() == reflect.Uint8 {
			return nil, fmt.Errorf("byte slice or array %s requires an explicit Shape", baseType)
		}
		if len(schema.enum) > 0 || hasStringConstraints(schema) || hasNumberConstraints(schema) || schema.format != "" {
			return nil, fmt.Errorf("array field has incompatible schema constraint")
		}
		elementSchema := schemaTag{required: true}
		elementShape, err := shapeForType(baseType.Elem(), elementSchema, input, active)
		if err != nil {
			return nil, fmt.Errorf("array item: %w", err)
		}
		shape = typedArrayShape{Items: elementShape, MinItems: schema.minItems, MaxItems: schema.maxItems}
	case reflect.Struct:
		if implementsCustomEncoding(baseType) {
			return nil, fmt.Errorf("custom JSON type %s requires an explicit Shape", baseType)
		}
		if len(schema.enum) > 0 || hasStringConstraints(schema) || hasNumberConstraints(schema) || hasItemConstraints(schema) || schema.format != "" {
			return nil, fmt.Errorf("object field has incompatible schema constraint")
		}
		object, err := compileStructShape(baseType, input, baseType.String(), active)
		if err != nil {
			return nil, err
		}
		shape = object
	case reflect.Map:
		return nil, fmt.Errorf("map type %s requires an explicit Shape", baseType)
	case reflect.Interface:
		return nil, fmt.Errorf("interface type %s requires an explicit Shape", baseType)
	default:
		return nil, fmt.Errorf("Go type %s cannot be mapped to a ValueShape", t)
	}
	if schema.nullable != nil && *schema.nullable {
		shape = typedOneOfShape{Variants: []typedValueShape{shape, typedNullShape{}}}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Replace the field with the encoded representation, e.g. use `string` for a time.Time (RFC 3339) and format the value before assigning.
  2. Provide an explicit Output.Data.Shape that declares the field as a string/number shape matching the encoding.
  3. If the type is structurally simple, remove the MarshalJSON/UnmarshalJSON methods (or use a plain alias struct) so the compiler can walk it.

Example fix

// before
type Data struct {
    CreatedAt time.Time `json:"created_at"`
}

// after
type Data struct {
    CreatedAt string `json:"created_at"` // t.Format(time.RFC3339)
}
Defensive patterns

Strategy: validation

Validate before calling

func implementsCustomEncoding(t reflect.Type) bool {
    for t.Kind() == reflect.Pointer { t = t.Elem() }
    return reflect.PointerTo(t).Implements(reflect.TypeFor[json.Marshaler]()) ||
        reflect.PointerTo(t).Implements(reflect.TypeFor[encoding.TextMarshaler]())
}
// reject any Data struct field whose type satisfies implementsCustomEncoding

Type guard

func isCustomJSONType(t reflect.Type) bool {
    return reflect.PointerTo(t).Implements(reflect.TypeFor[json.Marshaler]())
}

Prevention

When it happens

Trigger: A Data struct field whose type implements json.Marshaler or encoding.TextMarshaler (time.Time, url.URL, big.Int, custom ID types) hits shapeForType's reflect.Struct case during registration.

Common situations: Using time.Time directly for date fields; domain types with custom MarshalJSON reused as Data fields; generated types that embed custom encoders.

Related errors


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