XTLS/Xray-core · error
failed to get server spec
Error message
failed to get server spec
What it means
Returned by NewClient for the trojan outbound when protocol.NewServerSpecFromPB fails to convert the protobuf server definition into a runtime ServerSpec. It wraps the underlying error; typical base causes are an invalid destination (bad address/port) or an unparseable account entry in the config.
Source
Thrown at proxy/trojan/client.go:36
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/stat"
)
// Client is a inbound handler for trojan protocol
type Client struct {
server *protocol.ServerSpec
policyManager policy.Manager
}
// NewClient create a new trojan client.
func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
if config.Server == nil {
return nil, errors.New(`no target server found`)
}
server, err := protocol.NewServerSpecFromPB(config.Server)
if err != nil {
return nil, errors.New("failed to get server spec").Base(err)
}
v := core.MustFromContext(ctx)
client := &Client{
server: server,
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
}
return client, nil
}
// Process implements OutboundHandler.Process().
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
if !ob.Target.IsValid() {
return errors.New("target not specified")
}
ob.Name = "trojan"View on GitHub (pinned to 7d214f8b09)
Solutions
- Read the wrapped Base error — it pinpoints which field (address/port/account) failed.
- Fix address to a valid IP/hostname and port to 1-65535.
- Check the trojan settings schema in the current version and align field names (servers[].address/port/password).
- Run xray run -test against the config to catch it before runtime.
Example fix
// before
{ "address": "s.example.com:443", "port": 0 }
// after
{ "address": "s.example.com", "port": 443 } Defensive patterns
Strategy: validation
Validate before calling
func ValidateServerEntry(addr string, port uint32) error {
if addr == "" { return errors.New("address required") }
if port == 0 || port > 65535 { return errors.New("port out of range") }
if net.ParseIP(addr) == nil {
if _, err := idna.ToASCII(addr); err != nil { return err }
}
return nil
} Try / catch
if _, err := trojan.NewClient(ctx, cfg); err != nil {
if strings.Contains(err.Error(), "failed to get server spec") {
// surface the wrapped base error to the config author
}
} Prevention
- Validate address/port ranges before building server specs.
- Keep config field names aligned with the current core schema.
- Surface wrapped base errors in config tooling, not just the outer message.
When it happens
Trigger: The trojan outbound server entry exists but is malformed: empty address, port 0 or > 65535, wrong account type, or address that cannot be parsed into a net destination.
Common situations: Typo'd domain containing illegal characters; port field missing in JSON; users/password array shaped incorrectly; legacy config using outdated field names after a core upgrade.
Related errors
- unsupported domain strategy: {}
- invalid redirect address: {}
- invalid redirect port: {}
- HTTP settings: "servers" should have one and only one member
- version != 2
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/bc39bee8031be803.
Report an issue: GitHub.