{"record":{"id":"14fe01dfc47b6efd","repo":"wavetermdev/waveterm","slug":"invalid-service-q","errorCode":null,"errorMessage":"invalid service: %q","messagePattern":"invalid service: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/service/service.go","lineNumber":320,"sourceCode":"\t\t}\n\t\trtn.Data = val.Interface()\n\t}\n\tif rtn.Error == \"\" {\n\t\trtn.Success = true\n\t}\n\treturn rtn\n}\n\nfunc webErrorRtn(err error) *WebReturnType {\n\treturn &WebReturnType{\n\t\tError: err.Error(),\n\t}\n}\n\nfunc CallService(ctx context.Context, webCall WebCallType) *WebReturnType {\n\tsvcObj := ServiceMap[webCall.Service]\n\tif svcObj == nil {\n\t\treturn webErrorRtn(fmt.Errorf(\"invalid service: %q\", webCall.Service))\n\t}\n\tmethod := reflect.ValueOf(svcObj).MethodByName(webCall.Method)\n\tif !method.IsValid() {\n\t\treturn webErrorRtn(fmt.Errorf(\"invalid method: %s.%s\", webCall.Service, webCall.Method))\n\t}\n\tvar valueArgs []reflect.Value\n\targIdx := 0\n\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))","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/service/service.go#L302-L338","documentation":"CallService looks up the requested service name in the global ServiceMap; if webCall.Service is not registered, it returns this error quoting the unknown service name. It is the RPC layer's guard against dispatching to a nonexistent service object.","triggerScenarios":"Sending a WebCallType with a Service field that has no entry in ServiceMap — e.g. a typo (\"Client\" vs \"client\"), calling a service before its RegisterService init runs, or calling a service removed/renamed in a newer version.","commonSituations":"Frontend/backend version skew where the frontend calls a service the backend doesn't know; typos in service name strings; plugin code assuming a service exists without registering it; tests invoking CallService before test setup registers services.","solutions":["Verify the exact registered service name (check RegisterService calls / ServiceMap keys) and fix the Service field spelling/case.","Ensure the package registering the service is imported so its init() runs before the call.","Update the client if the service was renamed or removed in a newer version.","Add a startup assertion or list of known service names to catch drift early."],"exampleFix":"// before\nCallService(ctx, WebCallType{Service: \"clientcontrol\", ...})\n// unknown name\n\n// after\nCallService(ctx, WebCallType{Service: \"client\", ...})","handlingStrategy":"validation","validationCode":"const knownServices = ['client','fileshare','config', /* from ServiceMap registration */];\nif (!knownServices.includes(webCall.Service)) throw new Error(`unknown service: ${webCall.Service}`);","typeGuard":"function isKnownService(name) { return typeof name === 'string' && knownServices.includes(name); }","tryCatchPattern":"try {\n  const rtn = await callService(svc, method, args);\n  if (rtn.error?.startsWith('invalid service')) {\n    console.error('Service not registered or misnamed:', rtn.error);\n  }\n} catch (e) { /* log service name drift; check registration imports */ }","preventionTips":["Keep service name constants shared between client and server instead of raw strings.","Verify the package calling RegisterService is imported (init side effects).","Check for version skew when frontend and backend deploy independently."],"tags":["rpc","unknown-service","dispatch","registry"],"backgroundTag":"rpc-unknown-service","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}