wavetermdev/waveterm · error

cannot convert %T to %s (idx %d is not a map, is %T)

Error message

cannot convert %T to %s (idx %d is not a map, is %T)

What it means

When a service method takes []waveobj.WaveObj, convertSpecial first checks the whole argument is a JSON array, then iterates elements requiring each to be a JSON object. This error fires when element idx of the array is not an object (string, number, bool, null handled earlier, or nested array), reporting both the index and the offending element's Go type.

Source

Thrown at pkg/service/service.go:140

		}
		return oref, nil
	} else if argType == wsCommandRType {
		return convertWSCommand(argType, jsonArg)
	} else if argType == waveObjRType {
		if jsonType.Kind() != reflect.Map {
			return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
		}
		return waveobj.FromJsonMap(jsonArg.(map[string]any))
	} else if argType == waveObjSliceRType {
		if jsonType.Kind() != reflect.Slice {
			return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
		}
		sliceArg := jsonArg.([]any)
		nativeSlice := make([]waveobj.WaveObj, len(sliceArg))
		for idx, elem := range sliceArg {
			elemMap, ok := elem.(map[string]any)
			if !ok {
				return nil, fmt.Errorf("cannot convert %T to %s (idx %d is not a map, is %T)", jsonArg, waveObjSliceRType, idx, elem)
			}
			nativeObj, err := waveobj.FromJsonMap(elemMap)
			if err != nil {
				return nil, fmt.Errorf("cannot convert %T to %s (idx %d) error: %v", jsonArg, waveObjSliceRType, idx, err)
			}
			nativeSlice[idx] = nativeObj
		}
		return nativeSlice, nil
	} else if argType == waveObjMapRType {
		if jsonType.Kind() != reflect.Map {
			return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
		}
		mapArg := jsonArg.(map[string]any)
		nativeMap := make(map[string]waveobj.WaveObj)
		for key, elem := range mapArg {
			elemMap, ok := elem.(map[string]any)
			if !ok {
				return nil, fmt.Errorf("cannot convert %T to %s (key %s is not a map, is %T)", jsonArg, waveObjMapRType, key, elem)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Fix the array element at the reported idx to be a full serialized WaveObj object.
  2. Map any list of orefs/IDs to their full WaveObj representations before calling the RPC.
  3. Log the payload on the client to confirm array contents match the new schema.
  4. Check for a version skew between frontend caller and backend service method signatures.

Example fix

// before
callService("objservice", "UpdateObjects", ["block:default:abc"])
// after
callService("objservice", "UpdateObjects", [await rpcGetWaveObjJson("block:default:abc")])
Defensive patterns

Strategy: validation

Validate before calling

function isWaveObjArray(v) { return Array.isArray(v) && v.every(e => e && typeof e === 'object' && !Array.isArray(e) && typeof e.otype === 'string'); }
if (!isWaveObjArray(arg)) throw new Error('every array element must be a serialized WaveObj');

Type guard

const isWaveObjElem = (e) => typeof e === 'object' && e !== null && !Array.isArray(e);

Try / catch

try { return await callService(svc, method, args); } catch (e) { const m = String(e).match(/idx (\d+) is not a map, is (\S+)/); if (m) { console.error('bad WaveObj slice element at index', m[1], 'type', m[2]); } throw e; }

Prevention

When it happens

Trigger: An RPC call with a WaveObj slice argument where at least one array element is a scalar or nested array instead of a serialized WaveObj map — e.g. sending ["block:default:abc"] (oref strings) instead of [{"otype":...}].

Common situations: Frontend passing lists of object IDs/orefs instead of full objects; an API change where the endpoint now expects full objects but old callers still send IDs; JSON payloads built by string concatenation producing malformed arrays.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/a354262290e18d22. Report an issue: GitHub.