{"record":{"id":"b9d2f5286504222c","repo":"wavetermdev/waveterm","slug":"cannot-convert-t-to-s-key-s-is-not-a-map-is","errorCode":null,"errorMessage":"cannot convert %T to %s (key %s is not a map, is %T)","messagePattern":"cannot convert %T to (.+?) \\(key (.+?) is not a map, is %T\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/service/service.go","lineNumber":158,"sourceCode":"\t\t\t\treturn nil, fmt.Errorf(\"cannot convert %T to %s (idx %d is not a map, is %T)\", jsonArg, waveObjSliceRType, idx, elem)\n\t\t\t}\n\t\t\tnativeObj, err := waveobj.FromJsonMap(elemMap)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot convert %T to %s (idx %d) error: %v\", jsonArg, waveObjSliceRType, idx, err)\n\t\t\t}\n\t\t\tnativeSlice[idx] = nativeObj\n\t\t}\n\t\treturn nativeSlice, nil\n\t} else if argType == waveObjMapRType {\n\t\tif jsonType.Kind() != reflect.Map {\n\t\t\treturn nil, fmt.Errorf(\"cannot convert %T to %s\", jsonArg, argType)\n\t\t}\n\t\tmapArg := jsonArg.(map[string]any)\n\t\tnativeMap := make(map[string]waveobj.WaveObj)\n\t\tfor key, elem := range mapArg {\n\t\t\telemMap, ok := elem.(map[string]any)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot convert %T to %s (key %s is not a map, is %T)\", jsonArg, waveObjMapRType, key, elem)\n\t\t\t}\n\t\t\tnativeObj, err := waveobj.FromJsonMap(elemMap)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot convert %T to %s (key %s) error: %v\", jsonArg, waveObjMapRType, key, err)\n\t\t\t}\n\t\t\tnativeMap[key] = nativeObj\n\t\t}\n\t\treturn nativeMap, nil\n\t} else {\n\t\treturn nil, fmt.Errorf(\"invalid special wave argument type %s\", argType)\n\t}\n}\n\nfunc convertSpecialForReturn(argType reflect.Type, nativeArg any) (any, error) {\n\tif argType == waveObjRType {\n\t\treturn waveobj.ToJsonMap(nativeArg.(waveobj.WaveObj))\n\t} else if argType == waveObjSliceRType {\n\t\tnativeSlice := nativeArg.([]waveobj.WaveObj)","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/service/service.go#L140-L176","documentation":"For map[string]waveobj.WaveObj arguments, every value in the JSON map must itself be a JSON object decodable as a WaveObj. This error identifies the failing key and the offending value's Go type, e.g. when a map value is a string, number, or nested array instead of a serialized WaveObj.","triggerScenarios":"An RPC call with a WaveObj map argument where at least one value is not an object — commonly map values that are oref strings, object IDs, or booleans.","commonSituations":"Clients sending {\"key\": \"oref-string\"} or {\"key\": \"oid\"} maps instead of full object payloads; migrating an endpoint from ID-maps to object-maps while old callers persist; template or dynamically-built payloads injecting the wrong value type for one key.","solutions":["Replace the value at the reported key with a full serialized WaveObj object.","Resolve any orefs/IDs in the map to their full WaveObj JSON before the call.","Validate every map value is a plain object client-side before invoking.","Check for version skew between the caller and the service method's expected schema."],"exampleFix":"// before\ncallService(\"objservice\", \"SetObjects\", { \"main\": \"block:default:abc\" })\n// after\ncallService(\"objservice\", \"SetObjects\", { \"main\": await rpcGetWaveObjJson(\"block:default:abc\") })","handlingStrategy":"validation","validationCode":"for (const [k, v] of Object.entries(arg)) { if (!v || typeof v !== 'object' || Array.isArray(v)) throw new Error('map value at key ' + k + ' must be a serialized WaveObj object'); }","typeGuard":"const isWaveObjMapValue = (v) => typeof v === 'object' && v !== null && !Array.isArray(v) && typeof v.otype === 'string';","tryCatchPattern":"try { return await callService(svc, method, arg); } catch (e) { const m = String(e).match(/key (\\S+) is not a map, is (\\S+)/); if (m) { console.error('bad WaveObj map value at key', m[1], 'type', m[2]); } throw e; }","preventionTips":["Resolve every map value to a full WaveObj object, never a string/ID.","Validate all map values are plain objects before sending.","Keep map-typed endpoints free of legacy ID-map payloads.","Surface per-key conversion failures in client logs."],"tags":["rpc","type-conversion","map","waveobj"],"backgroundTag":"json-type-mismatch","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}