{"record":{"id":"95700abd00c327f7","repo":"XTLS/Xray-core","slug":"cannot-finish-connection-95700a","errorCode":null,"errorMessage":"cannot finish connection","messagePattern":"cannot finish connection","errorType":"console","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"app/observatory/observer.go","lineNumber":154,"sourceCode":"\t\t},\n\t\tDialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {\n\t\t\tvar connection net.Conn\n\t\t\ttaskErr := task.Run(ctx, func() error {\n\t\t\t\t// MUST use Xray's built in context system\n\t\t\t\tdest, err := v2net.ParseDestination(network + \":\" + addr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.New(\"cannot understand address\").Base(err)\n\t\t\t\t}\n\t\t\t\ttrackedCtx := session.TrackedConnectionError(o.ctx, errorCollectorForRequest)\n\t\t\t\tconn, err := tagged.Dialer(trackedCtx, o.dispatcher, dest, outbound)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.New(\"cannot dial remote address \", dest).Base(err)\n\t\t\t\t}\n\t\t\t\tconnection = conn\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\tif taskErr != nil {\n\t\t\t\treturn nil, errors.New(\"cannot finish connection\").Base(taskErr)\n\t\t\t}\n\t\t\treturn connection, nil\n\t\t},\n\t\tTLSHandshakeTimeout: time.Second * 5,\n\t}\n\thttpClient := &http.Client{\n\t\tTransport: &httpTransport,\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t\tJar:     nil,\n\t\tTimeout: time.Second * 5,\n\t}\n\tvar GETTime time.Duration\n\terr := task.Run(o.ctx, func() error {\n\t\tstartTime := time.Now()\n\t\tprobeURL := \"https://www.google.com/generate_204\"\n\t\tif o.config.ProbeUrl != \"\" {","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/observatory/observer.go#L136-L172","documentation":"Thrown in app/observatory/observer.go:154 as the outer wrapper returned from DialContext when the task.Run closure (parse + dial) returned any error. It is the boundary translation between Xray's task error and Go's net.Dial return, so its Base chain contains either 'cannot understand address' or 'cannot dial remote address' plus the root cause.","triggerScenarios":"Always produced together with errors 45/46: any failure of the inner parse-or-dial task surfaces to Go's http.Transport as 'cannot finish connection'. This message is the envelope; the real cause is one level down in Base().","commonSituations":"Appears in the middle of probe failure chains; by itself it only tells you the probe connection setup failed, and users should always read the chained error.","solutions":["Inspect err.Base() to find whether it was an address parse issue or a dial failure, then follow the corresponding fix.","Verify the outbound server and its streamSettings.","Confirm the probe URL is well-formed."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// always unwrap: the wrapper has no independent information\nif err != nil {\n    root := err\n    for errors.Unwrap(root) != nil { root = errors.Unwrap(root) }\n    handleByRootCause(root)\n}","preventionTips":["Never branch on the 'cannot finish connection' text itself; branch on the chained cause.","Log with %v under a multi-error-aware formatter."],"tags":["go","xray","observatory","error-chaining","outbound"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}