{"record":{"id":"0dd94e91f1b1e0b9","repo":"openimsdk/open-im-server","slug":"signal-s","errorCode":null,"errorMessage":"signal %s","messagePattern":"signal (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"pkg/common/startrpc/start.go","lineNumber":171,"sourceCode":"\t\tgrpccli.GrpcClientLogger(),\n\t\tgrpccli.GrpcClientContext(),\n\t\tgrpccli.GrpcClientErrorConvert(),\n\t)\n\tif len(clientOptions) > 0 {\n\t\tclient.AddOption(clientOptions...)\n\t}\n\n\tctx, cancel := context.WithCancelCause(ctx)\n\n\tgo func() {\n\t\tsigs := make(chan os.Signal, 1)\n\t\tsignal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase val := <-sigs:\n\t\t\tlog.ZDebug(ctx, \"recv signal\", \"signal\", val.String())\n\t\t\tcancel(fmt.Errorf(\"signal %s\", val.String()))\n\t\t}\n\t}()\n\n\tif prometheusListenAddr != \"\" {\n\t\toptions = append(\n\t\t\toptions,\n\t\t\tprommetricsUnaryInterceptor(rpcRegisterName),\n\t\t\tprommetricsStreamInterceptor(rpcRegisterName),\n\t\t)\n\t\tprometheusListener, prometheusPort, err := listenTCP(prometheusListenAddr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tlog.ZDebug(ctx, \"prometheus start\", \"addr\", prometheusListener.Addr(), \"rpcRegisterName\", rpcRegisterName)\n\t\ttarget, err := jsonutil.JsonMarshal(prommetrics.BuildDefaultTarget(registerIP, prometheusPort))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/openimsdk/open-im-server/blob/175a7bb0673eca18e9d1b10bff4f728da6b1b513/pkg/common/startrpc/start.go#L153-L189","documentation":"In Start, a goroutine watches SIGTERM/SIGINT; when one arrives it cancels the root context with a cause error 'signal <name>'. This error is the cancellation cause, not an internal fault — it is how the process communicates why it is shutting down (via context.Cause).","triggerScenarios":"The process receives SIGTERM (e.g. kubectl delete pod, systemd stop, docker stop) or SIGINT (Ctrl+C) while the RPC server is running.","commonSituations":"Normal graceful shutdown in Kubernetes (SIGTERM on pod termination); developer pressing Ctrl+C locally; orchestrator restarts; logs/context.Cause then surface 'signal terminated' or 'signal interrupt'.","solutions":["No fix needed — this is the expected graceful shutdown path.","Use context.Cause(ctx) after shutdown to log the reason; treat 'signal terminated/interrupt' as normal exit.","If shutdown is unexpected, check what sent the signal: Kubernetes events (OOM/liveness kill shows as different exit reason), systemd, or local terminal.","Ensure signal handling completes cleanup before exit since ctx is cancelled immediately on signal."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := start(ctx)\nif cause := context.Cause(ctx); cause != nil {\n    var sigErr *fmt.wrapError // or string-match on \"signal \"\n    if strings.HasPrefix(cause.Error(), \"signal \") {\n        log.Info(\"graceful shutdown\", \"cause\", cause)\n        return nil\n    }\n}","preventionTips":["Treat 'signal terminated'/'signal interrupt' causes as normal exit code 0","Distinguish Kubernetes SIGTERM (rollout) from crashes via exit reason","Ensure cleanup runs promptly after context cancellation"],"tags":["signal","shutdown","lifecycle"],"backgroundTag":"graceful-shutdown-signal","analyzedSha":"175a7bb0673eca18e9d1b10bff4f728da6b1b513","analyzedAt":"2026-09-04T16:52:56.821Z","contentChangedAt":"2026-09-04T16:52:56.821Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}