larksuite/cli · error
interface type %s requires an explicit Shape
Error message
interface type %s requires an explicit Shape
What it means
Interface-typed fields (other than the top-level `any` Data escape hatch) have no static schema, so the compiler cannot infer a ValueShape and rejects them. Only Output.Data itself may be an empty interface; nested interface fields must be replaced by concrete types or an explicit Shape.
Source
Thrown at shortcuts/common/typed_compile_data.go:167
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{}}}
}
return shape, nil
}
// compileStructShape walks one struct into an ObjectShape. active holds the
// struct types already open on the current recursion path, so a type that
// refers back to itself is reported as a compile error. Without the guard the
// walk never terminates and the goroutine stack is exhausted -- that is a
// fatal runtime error, not a panic, so no recover boundary can contain it and
// the whole CLI dies during command registration.
//
// Membership is scoped to the path rather than the whole walk: a type is
// removed once its fields are compiled, so the same type appearing twice as aView on GitHub (pinned to 7fd6ef3c07)
Solutions
- Replace the interface field with a concrete struct or scalar type that models the actual content.
- Provide an explicit Output.Data.Shape describing the field.
- If the payload is genuinely arbitrary JSON, promote it: make Output.Data itself `any` so anyJSONShape applies.
- Split Data into variant structs (or use Overrides) instead of one interface-typed field.
Example fix
// before
type Data struct {
Payload interface{} `json:"payload"`
}
// after
type Payload struct {
Key string `json:"key"`
Value string `json:"value"`
}
type Data struct {
Payload Payload `json:"payload"` Defensive patterns
Strategy: type-guard
Validate before calling
func hasInterfaceField(t reflect.Type) bool {
for t.Kind() == reflect.Pointer { t = t.Elem() }
switch t.Kind() {
case reflect.Interface:
return t.NumMethod() > 0 || t != t // any non-top-level interface
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
if hasInterfaceField(t.Field(i).Type) { return true }
}
case reflect.Slice, reflect.Array:
return hasInterfaceField(t.Elem())
}
return false
} Type guard
func isInterface(t reflect.Type) bool { return t.Kind() == reflect.Interface } Prevention
- Use concrete structs instead of interface{} for nested Data fields.
- Only Output.Data itself may be `any`; everything nested must be concrete.
- Keep test doubles (mock interfaces) out of wire structs.
When it happens
Trigger: A nested Data struct field typed interface{}, io.Reader-like interfaces, or any non-empty interface is hit by shapeForType's reflect.Interface case during registration.
Common situations: Placeholder `Details interface{}` fields in DTOs; interfaces used for testability leaking into wire structs; converting loosely typed response structs into typed Data.
Related errors
- json.RawMessage requires an explicit Shape
- byte slice or array %s requires an explicit Shape
- custom JSON type %s requires an explicit Shape
- map type %s requires an explicit Shape
- Go type %s cannot be mapped to a ValueShape
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/b46f787f4f6967cb.
Report an issue: GitHub.