{"record":{"id":"925f4d210bd79497","repo":"XTLS/Xray-core","slug":"failed-to-close-logger","errorCode":null,"errorMessage":"failed to close logger","messagePattern":"failed to close logger","errorType":"exception","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"app/log/command/command.go","lineNumber":24,"sourceCode":"\t\"github.com/xtls/xray-core/app/log\"\n\t\"github.com/xtls/xray-core/common\"\n\t\"github.com/xtls/xray-core/common/errors\"\n\t\"github.com/xtls/xray-core/core\"\n\tgrpc \"google.golang.org/grpc\"\n)\n\ntype LoggerServer struct {\n\tV *core.Instance\n}\n\n// RestartLogger implements LoggerService.\nfunc (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) {\n\tlogger := s.V.GetFeature((*log.Instance)(nil))\n\tif logger == nil {\n\t\treturn nil, errors.New(\"unable to get logger instance\")\n\t}\n\tif err := logger.Close(); err != nil {\n\t\treturn nil, errors.New(\"failed to close logger\").Base(err)\n\t}\n\tif err := logger.Start(); err != nil {\n\t\treturn nil, errors.New(\"failed to start logger\").Base(err)\n\t}\n\treturn &RestartLoggerResponse{}, nil\n}\n\nfunc (s *LoggerServer) mustEmbedUnimplementedLoggerServiceServer() {}\n\ntype service struct {\n\tv *core.Instance\n}\n\nfunc (s *service) Register(server *grpc.Server) {\n\tls := &LoggerServer{\n\t\tV: s.v,\n\t}\n\tRegisterLoggerServiceServer(server, ls)","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/log/command/command.go#L6-L42","documentation":"RestartLogger closes the existing log.Instance before starting it again; if logger.Close() returns an error it is wrapped with this message. Close flushes and releases file log writers, so failures usually come from the underlying file (already closed, permission removed, disk gone).","triggerScenarios":"RestartLogger invoked when the log file was deleted, its directory unmounted, permissions changed, or the writer already closed by a concurrent restart.","commonSituations":"logrotate moving the file away while the API restart fires; read-only filesystems in containers; two concurrent RestartLogger calls racing on the same instance.","solutions":["Check the chained Base error for the exact file operation that failed","Restore write permission / recreate the log directory and retry the restart","Configure logrotate with copytruncate or signal Xray instead of deleting the file out from under it"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before restarting, confirm the log file is still writable:\nif access := logger.AccessLogPath(); access != \"\" {\n    if f, err := os.OpenFile(access, os.O_APPEND|os.O_WRONLY, 0o644); err != nil {\n        // fix path/permissions before calling RestartLogger\n    } else { f.Close() }\n}","typeGuard":null,"tryCatchPattern":"resp, err := loggerClient.RestartLogger(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"failed to close logger\") {\n    // inspect grpc details for the base file error; restore the log file and retry once\n}","preventionTips":["Use logrotate copytruncate or HUP instead of deleting live log files","Ensure the log directory persists across container restarts","Avoid concurrent RestartLogger calls"],"tags":["api","grpc","logging","file-io"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}