{"record":{"id":"bc39bee8031be803","repo":"XTLS/Xray-core","slug":"failed-to-get-server-spec-bc39be","errorCode":null,"errorMessage":"failed to get server spec","messagePattern":"failed to get server spec","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/trojan/client.go","lineNumber":36,"sourceCode":"\t\"github.com/xtls/xray-core/transport\"\n\t\"github.com/xtls/xray-core/transport/internet\"\n\t\"github.com/xtls/xray-core/transport/internet/stat\"\n)\n\n// Client is a inbound handler for trojan protocol\ntype Client struct {\n\tserver        *protocol.ServerSpec\n\tpolicyManager policy.Manager\n}\n\n// NewClient create a new trojan client.\nfunc NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {\n\tif config.Server == nil {\n\t\treturn nil, errors.New(`no target server found`)\n\t}\n\tserver, err := protocol.NewServerSpecFromPB(config.Server)\n\tif err != nil {\n\t\treturn nil, errors.New(\"failed to get server spec\").Base(err)\n\t}\n\n\tv := core.MustFromContext(ctx)\n\tclient := &Client{\n\t\tserver:        server,\n\t\tpolicyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),\n\t}\n\treturn client, nil\n}\n\n// Process implements OutboundHandler.Process().\nfunc (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {\n\toutbounds := session.OutboundsFromContext(ctx)\n\tob := outbounds[len(outbounds)-1]\n\tif !ob.Target.IsValid() {\n\t\treturn errors.New(\"target not specified\")\n\t}\n\tob.Name = \"trojan\"","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/trojan/client.go#L18-L54","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{ \"address\": \"s.example.com:443\", \"port\": 0 }\n\n// after\n{ \"address\": \"s.example.com\", \"port\": 443 }","handlingStrategy":"validation","validationCode":"func ValidateServerEntry(addr string, port uint32) error {\n\tif addr == \"\" { return errors.New(\"address required\") }\n\tif port == 0 || port > 65535 { return errors.New(\"port out of range\") }\n\tif net.ParseIP(addr) == nil {\n\t\tif _, err := idna.ToASCII(addr); err != nil { return err }\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := trojan.NewClient(ctx, cfg); err != nil {\n\tif strings.Contains(err.Error(), \"failed to get server spec\") {\n\t\t// surface the wrapped base error to the config author\n\t}\n}","preventionTips":["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."],"tags":["trojan","outbound","config","server-spec","validation"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}