wavetermdev/waveterm · error

invalid map key type %s

Error message

invalid map key type %s

What it means

convertArgument rejects map-typed method parameters whose Go key type is not string. JSON objects always deserialize to map[string]any, so only map[string]X parameters are convertible; e.g. map[int]string or map[CustomKey]T can never be populated from a JSON RPC call and fail immediately.

Source

Thrown at pkg/service/service.go:233

		return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)

	case reflect.Bool:
		if jsonType.Kind() == reflect.Bool {
			return jsonArg, nil
		}
		return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64:
		if jsonType.Kind() == reflect.Float64 {
			return convertNumber(argType, jsonArg.(float64))
		}
		return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)

	case reflect.Map:
		if argType.Key().Kind() != reflect.String {
			return nil, fmt.Errorf("invalid map key type %s", argType.Key())
		}
		if jsonType.Kind() != reflect.Map {
			return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
		}
		return convertComplex(argType, jsonArg)

	case reflect.Slice:
		if jsonType.Kind() != reflect.Slice {
			return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
		}
		return convertComplex(argType, jsonArg)

	case reflect.Struct:
		if jsonType.Kind() != reflect.Map {
			return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
		}
		return convertComplex(argType, jsonArg)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Change the service method signature to use map[string]T and convert keys (e.g. strconv.Atoi) inside the method body.
  2. Wrap the non-string-key map behind a string-keyed DTO type accepted by the RPC layer.
  3. If the key is a complex type, accept a slice of {key,value} pairs and rebuild the map server-side.

Example fix

// before
func (s *MyService) SetScores(m map[int]int) error

// after
func (s *MyService) SetScores(m map[string]int) error {
    scores := map[int]int{}
    for k, v := range m {
        i, _ := strconv.Atoi(k)
        scores[i] = v
    }
    ...
}
Defensive patterns

Strategy: validation

Validate before calling

if (typeof arg !== 'object' || Array.isArray(arg)) throw new Error('map param expects a JSON object with string keys');

Type guard

function isStringKeyedRecord(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }

Try / catch

try {
  const rtn = await callService(svc, method, args);
  if (rtn.error?.includes('invalid map key type')) throw new Error('method uses unsupported non-string map keys');
} catch (e) { /* switch to string-keyed DTO or report unsupported API */ }

Prevention

When it happens

Trigger: A registered service method declares a parameter of kind reflect.Map whose key kind is not string (e.g. map[int]bool, map[uuid.UUID]T), and a client calls that method through CallService.

Common situations: Service authors writing methods with non-string map keys (numeric IDs, custom key structs) then exposing them over RPC; refactoring a method to use a richer key type without realizing the RPC bridge only supports string keys.

Related errors


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