XTLS/Xray-core · error · errors.Error

unable to create log handler for

Error message

unable to create log handler for 

What it means

createHandler looks up a creator function in handlerCreatorMap by LogType; an unknown/unregistered type yields this error with the offending logType embedded. Console and File creators are registered in init(); any other value (including an unparsed string form) has no handler.

Source

Thrown at app/log/log_creator.go:39

func RegisterHandlerCreator(logType LogType, f HandlerCreator) error {
	if f == nil {
		return errors.New("nil HandlerCreator")
	}

	handlerCreatorMapLock.Lock()
	defer handlerCreatorMapLock.Unlock()

	handlerCreatorMap[logType] = f
	return nil
}

func createHandler(logType LogType, options HandlerCreatorOptions) (log.Handler, error) {
	handlerCreatorMapLock.RLock()
	defer handlerCreatorMapLock.RUnlock()

	creator, found := handlerCreatorMap[logType]
	if !found {
		return nil, errors.New("unable to create log handler for ", logType)
	}
	return creator(logType, options)
}

func init() {
	common.Must(RegisterHandlerCreator(LogType_Console, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) {
		return log.NewLogger(log.CreateStdoutLogWriter()), nil
	}))

	common.Must(RegisterHandlerCreator(LogType_File, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) {
		creator, err := log.CreateFileLogWriter(options.Path)
		if err != nil {
			return nil, err
		}
		return log.NewLogger(creator), nil
	}))

	common.Must(RegisterHandlerCreator(LogType_None, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Use only 'console' or 'file' as the log type in the config
  2. If a custom handler type is needed, call RegisterHandlerCreator for that LogType before creating the log instance
  3. Rebuild/upgrade so the binary and the config's log type vocabulary match

Example fix

// before (embedding Xray-core)
logType := LogType(42) // unregistered
handler, err := createHandler(logType, options) // error

// after
logType := LogType_File
handler, err := createHandler(logType, options)
Defensive patterns

Strategy: validation

Validate before calling

// Restrict config values to registered handler types before creating the logger:
switch logType {
case LogType_Console, LogType_File:
    // ok
default:
    return fmt.Errorf("unsupported log type: %v", logType)
}

Type guard

func isRegisteredLogType(t LogType) bool {
    return t == LogType_Console || t == LogType_File
}

Prevention

When it happens

Trigger: RegisterHandlerCreator was never called for the requested LogType — e.g. a config/protobuf supplies a LogType enum value outside LogType_Console/LogType_File, or a custom build skipped the init() registrations.

Common situations: Version skew where a newer config format carries a log type this binary does not know; typos when constructing LogType programmatically; stripped builds that exclude the default handler registrations.

Related errors


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