XTLS/Xray-core · error · errors.Error

failed to close logger

Error message

failed to close logger

What it means

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).

Source

Thrown at app/log/command/command.go:24

	"github.com/xtls/xray-core/app/log"
	"github.com/xtls/xray-core/common"
	"github.com/xtls/xray-core/common/errors"
	"github.com/xtls/xray-core/core"
	grpc "google.golang.org/grpc"
)

type LoggerServer struct {
	V *core.Instance
}

// RestartLogger implements LoggerService.
func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) {
	logger := s.V.GetFeature((*log.Instance)(nil))
	if logger == nil {
		return nil, errors.New("unable to get logger instance")
	}
	if err := logger.Close(); err != nil {
		return nil, errors.New("failed to close logger").Base(err)
	}
	if err := logger.Start(); err != nil {
		return nil, errors.New("failed to start logger").Base(err)
	}
	return &RestartLoggerResponse{}, nil
}

func (s *LoggerServer) mustEmbedUnimplementedLoggerServiceServer() {}

type service struct {
	v *core.Instance
}

func (s *service) Register(server *grpc.Server) {
	ls := &LoggerServer{
		V: s.v,
	}
	RegisterLoggerServiceServer(server, ls)

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Check the chained Base error for the exact file operation that failed
  2. Restore write permission / recreate the log directory and retry the restart
  3. Configure logrotate with copytruncate or signal Xray instead of deleting the file out from under it
Defensive patterns

Strategy: try-catch

Validate before calling

// Before restarting, confirm the log file is still writable:
if access := logger.AccessLogPath(); access != "" {
    if f, err := os.OpenFile(access, os.O_APPEND|os.O_WRONLY, 0o644); err != nil {
        // fix path/permissions before calling RestartLogger
    } else { f.Close() }
}

Try / catch

resp, err := loggerClient.RestartLogger(ctx, req)
if err != nil && strings.Contains(err.Error(), "failed to close logger") {
    // inspect grpc details for the base file error; restore the log file and retry once
}

Prevention

When it happens

Trigger: RestartLogger invoked when the log file was deleted, its directory unmounted, permissions changed, or the writer already closed by a concurrent restart.

Common situations: logrotate moving the file away while the API restart fires; read-only filesystems in containers; two concurrent RestartLogger calls racing on the same instance.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/925f4d210bd79497. Report an issue: GitHub.