XTLS/Xray-core · error

failed to read config:

Error message

failed to read config: 

What it means

The JSON config loader (registered for the "json" format) fails when confloader.LoadConfig cannot produce a reader for one of the argument paths. confloader.LoadConfig dispatches to external.FetchHTTPContent for http(s)/socket-looking targets and to local file reading otherwise, so this error wraps either a network fetch failure or a filesystem error (missing file, permission denied).

Source

Thrown at main/json/json.go:28

	"github.com/xtls/xray-core/core"
	"github.com/xtls/xray-core/infra/conf"
	"github.com/xtls/xray-core/infra/conf/serial"
	"github.com/xtls/xray-core/main/confloader"
)

func init() {
	common.Must(core.RegisterConfigLoader(&core.ConfigFormat{
		Name:      "JSON",
		Extension: []string{"json"},
		Loader: func(input interface{}) (*core.Config, error) {
			switch v := input.(type) {
			case cmdarg.Arg:
				cf := &conf.Config{}
				for i, arg := range v {
					errors.LogInfo(context.Background(), "Reading config: ", arg)
					r, err := confloader.LoadConfig(arg)
					if err != nil {
						return nil, errors.New("failed to read config: ", arg).Base(err)
					}
					c, err := serial.DecodeJSONConfig(r)
					if err != nil {
						return nil, errors.New("failed to decode config: ", arg).Base(err)
					}
					if i == 0 {
						// This ensure even if the muti-json parser do not support a setting,
						// It is still respected automatically for the first configure file
						*cf = *c
						continue
					}
					cf.Override(c, arg)
				}
				return cf.Build()
			case io.Reader:
				if serial.UseStrictJSON {
					cfg, err := serial.DecodeJSONConfigStrict(v)
					if err != nil {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Confirm the file exists at the exact path printed in the message (it includes the failing arg)
  2. Fix permissions: chmod 644 and ensure the running user owns/can read it
  3. Use absolute paths in service/systemd units where the working directory differs
  4. If the arg is a URL, debug the underlying fetch error (dial/status/read) chained in Base(err)

Example fix

# before
xray run -c conf/config.json   # run from another cwd -> failed to read config

# after
xray run -c /etc/xray/config.json
Defensive patterns

Strategy: validation

Validate before calling

func configReadable(p string) error { fi, err := os.Stat(p); if err != nil { return err }; if fi.IsDir() { return fmt.Errorf("is dir") }; return nil }

for _, p := range paths { if err := configReadable(p); err != nil { log.Fatal(err) } }

Type guard

func isLoadableArg(v interface{}) bool { _, ok1 := v.(cmdarg.Arg); _, ok2 := v.(io.Reader); return ok1 || ok2 }

Try / catch

if err := runStartXray(); err != nil && strings.Contains(err.Error(), "failed to read config") { os.Exit(1) /* report file from message */ }

Prevention

When it happens

Trigger: Passing a nonexistent path in cmdarg.Arg (e.g. xray run -c ./missing.json), a file without read permission, or a URL-style config target whose fetch failed (dial, status, read errors from errors 541-543 surface here as the chained cause).

Common situations: Wrong working directory making a relative config path unresolvable, typos in filenames, running Xray as a service user that cannot read the config, or remote-config URL failures at boot.

Related errors


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