{"record":{"id":"26743e060b82543d","repo":"larksuite/cli","slug":"go-type-s-cannot-be-mapped-to-a-valueshape","errorCode":null,"errorMessage":"Go type %s cannot be mapped to a ValueShape","messagePattern":"Go type (.+?) cannot be mapped to a ValueShape","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/common/typed_compile_data.go","lineNumber":169,"sourceCode":"\t\tshape = typedArrayShape{Items: elementShape, MinItems: schema.minItems, MaxItems: schema.maxItems}\n\tcase reflect.Struct:\n\t\tif implementsCustomEncoding(baseType) {\n\t\t\treturn nil, fmt.Errorf(\"custom JSON type %s requires an explicit Shape\", baseType)\n\t\t}\n\t\tif len(schema.enum) > 0 || hasStringConstraints(schema) || hasNumberConstraints(schema) || hasItemConstraints(schema) || schema.format != \"\" {\n\t\t\treturn nil, fmt.Errorf(\"object field has incompatible schema constraint\")\n\t\t}\n\t\tobject, err := compileStructShape(baseType, input, baseType.String(), active)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tshape = object\n\tcase reflect.Map:\n\t\treturn nil, fmt.Errorf(\"map type %s requires an explicit Shape\", baseType)\n\tcase reflect.Interface:\n\t\treturn nil, fmt.Errorf(\"interface type %s requires an explicit Shape\", baseType)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"Go type %s cannot be mapped to a ValueShape\", t)\n\t}\n\tif schema.nullable != nil && *schema.nullable {\n\t\tshape = typedOneOfShape{Variants: []typedValueShape{shape, typedNullShape{}}}\n\t}\n\treturn shape, nil\n}\n\n// compileStructShape walks one struct into an ObjectShape. active holds the\n// struct types already open on the current recursion path, so a type that\n// refers back to itself is reported as a compile error. Without the guard the\n// walk never terminates and the goroutine stack is exhausted -- that is a\n// fatal runtime error, not a panic, so no recover boundary can contain it and\n// the whole CLI dies during command registration.\n//\n// Membership is scoped to the path rather than the whole walk: a type is\n// removed once its fields are compiled, so the same type appearing twice as a\n// sibling stays legal.\nfunc compileStructShape(t reflect.Type, input bool, path string, active map[reflect.Type]struct{}) (typedObjectShape, error) {","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/common/typed_compile_data.go#L151-L187","documentation":"The compiler's reflect-based switch ran out of kinds: the field's Go type is something (after pointer unwrapping) that has no JSON mapping in the shape system — e.g. func, chan, complex numbers, or unsafe pointers. It cannot be mapped to a ValueShape, so registration fails with the concrete type named.","triggerScenarios":"A Data struct (or nested field reached via collectArgFields/shapeForType) declares a field of an unmappable kind such as func, chan, complex128, or uintptr.","commonSituations":"Callback/handle fields accidentally included in wire structs; complex numbers from math code; generated structs containing runtime-only members.","solutions":["Remove the field from Data or move it behind `json:\"-\"` if it is not part of the wire payload.","Replace it with the JSON-representable form (e.g. store an opaque handle as string).","Restructure so only plain JSON-mappable types (string/bool/int/float/slice/struct) appear in Data.","For pointer-typed fields remember pointers are unwrapped; the error names the base type — fix the base kind."],"exampleFix":"// before\ntype Data struct {\n    Hook func() `json:\"hook\"` // unmappable\n}\n\n// after\ntype Data struct {\n    Hook string `json:\"hook\"` // opaque handle/id\n}","handlingStrategy":"validation","validationCode":"func mappable(t reflect.Type) bool {\n    for t.Kind() == reflect.Pointer { t = t.Elem() }\n    switch t.Kind() {\n    case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,\n        reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16,\n        reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64,\n        reflect.Slice, reflect.Array, reflect.Struct:\n        if t.Kind() == reflect.Struct {\n            for i := 0; i < t.NumField(); i++ {\n                if !mappable(t.Field(i).Type) { return false }\n            }\n        }\n        if t.Kind() == reflect.Slice || t.Kind() == reflect.Array {\n            return mappable(t.Elem())\n        }\n        return true\n    }\n    return false // func, chan, complex, etc.\n}","typeGuard":"func isMappableKind(k reflect.Kind) bool {\n    switch k {\n    case reflect.String, reflect.Bool, reflect.Int, reflect.Int64, reflect.Float64,\n        reflect.Slice, reflect.Struct:\n        return true\n    }\n    return false\n}","tryCatchPattern":null,"preventionTips":["Exclude funcs, channels, and complex numbers from wire structs; use `json:\"-\"` if they must exist on the type.","Keep runtime-only state in a separate struct from Data.","Run a reflect-based mappability check in tests over every Data type."],"tags":["go","reflect","schema","typed-data"],"backgroundTag":"explicit-shape-required","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}