{"record":{"id":"99fa10f50a560f76","repo":"wavetermdev/waveterm","slug":"cannot-convert-argument-s-s-type-t-idx-d-error","errorCode":null,"errorMessage":"cannot convert argument %s.%s type:%T idx:%d error:%v","messagePattern":"cannot convert argument (.+?)\\.(.+?) type:%T idx:(.+?) error:(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/service/service.go","lineNumber":346,"sourceCode":"\tfor idx := 0; idx < method.Type().NumIn(); idx++ {\n\t\targType := method.Type().In(idx)\n\t\tif idx == 0 && argType == contextRType {\n\t\t\tvalueArgs = append(valueArgs, reflect.ValueOf(ctx))\n\t\t\tcontinue\n\t\t}\n\t\tif argType == uiContextRType {\n\t\t\tif webCall.UIContext == nil {\n\t\t\t\treturn webErrorRtn(fmt.Errorf(\"missing UIContext for %s.%s\", webCall.Service, webCall.Method))\n\t\t\t}\n\t\t\tvalueArgs = append(valueArgs, reflect.ValueOf(*webCall.UIContext))\n\t\t\tcontinue\n\t\t}\n\t\tif argIdx >= len(webCall.Args) {\n\t\t\treturn webErrorRtn(fmt.Errorf(\"not enough arguments passed %s.%s idx:%d (type %T)\", webCall.Service, webCall.Method, idx, argType))\n\t\t}\n\t\tnativeArg, err := convertArgument(argType, webCall.Args[argIdx])\n\t\tif err != nil {\n\t\t\treturn webErrorRtn(fmt.Errorf(\"cannot convert argument %s.%s type:%T idx:%d error:%v\", webCall.Service, webCall.Method, argType, idx, err))\n\t\t}\n\t\tvalueArgs = append(valueArgs, reflect.ValueOf(nativeArg))\n\t\targIdx++\n\t}\n\tretValArr := method.Call(valueArgs)\n\treturn convertReturnValues(retValArr)\n}\n\n// ValidateServiceArg validates the argument type for a service method\n// does not allow interfaces (and the obvious invalid types)\n// arguments + return values have special handling for wave objects\nfunc baseValidateServiceArg(argType reflect.Type) error {\n\tif argType == waveObjUpdateRType {\n\t\t// has special MarshalJSON method, so it is safe\n\t\treturn nil\n\t}\n\tswitch argType.Kind() {\n\tcase reflect.Ptr, reflect.Slice, reflect.Array:","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/service/service.go#L328-L364","documentation":"Each positional argument from webCall.Args is converted from its JSON (interface{}) form to the method's declared parameter type via convertArgument. This error wraps the failure of that conversion: the JSON value's type/shape does not match the parameter type (e.g. a string where a number, a non-object where a struct, or malformed JSON for a typed field).","triggerScenarios":"Passing an argument of the wrong JSON type — e.g. sending a string \"123\" where an int is required, a number where a bool is required, or an object missing required fields for a struct parameter; also nested type mismatches inside struct args.","commonSituations":"Hand-written RPC calls with untyped JSON (JavaScript numbers-as-strings); frontend sending null for non-pointer parameters; schema drift where the frontend still sends the old field format for a struct argument; locale/encoding issues corrupting string args.","solutions":["Match each arg's JSON type to the method signature: string->string, number->int/float64, boolean->bool, object->struct, and fix the caller's payload","Look at the wrapped %v error and the idx/type in the message to pinpoint the offending argument and its expected Go type","Regenerate the TypeScript client so argument types are enforced at compile time in the frontend","For struct parameters, ensure the JSON object's fields marshal to the Go struct (field names via the Go JSON tags)"],"exampleFix":"// before\nArgs: [\"123\", \"true\"] // strings where int and bool expected\n// after\nArgs: [123, true]","handlingStrategy":"validation","validationCode":"function assertArgTypes(call, expected) {\n  call.Args.forEach((arg, i) => {\n    const t = expected[i];\n    if (t === \"number\" && typeof arg !== \"number\") throw new Error(`arg ${i}: expected number, got ${typeof arg}`);\n    if (t === \"string\" && typeof arg !== \"string\") throw new Error(`arg ${i}: expected string, got ${typeof arg}`);\n    if (t === \"boolean\" && typeof arg !== \"boolean\") throw new Error(`arg ${i}: expected boolean, got ${typeof arg}`);\n  });\n}","typeGuard":"function isArgOf<T>(arg: unknown, chk: (a: unknown) => a is T): arg is T { return chk(arg); }\nconst isNum = (a: unknown): a is number => typeof a === \"number\" && !isNaN(a);","tryCatchPattern":"const rtn = CallService(ctx, call);\nif (rtn.Error.startsWith(\"cannot convert argument\")) {\n  const idx = parseInt(rtn.Error.match(/idx:(\\d+)/)?.[1] ?? \"-1\", 10);\n  console.error(`bad argument at position ${idx - offset}:`, call.Args[idx]);\n}","preventionTips":["Never stringify numbers/booleans before sending as RPC args","Parse user input into typed values (Number(), Boolean()) before dispatching","Keep the generated TS client in sync so types are enforced statically","For struct args, validate objects against their JSON schema/TS interface before sending"],"tags":["rpc","type-conversion","json"],"backgroundTag":"argument-type-mismatch","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}