{"record":{"id":"4d1933b36e273cfb","repo":"XTLS/Xray-core","slug":"missing-inbound-or-outbound-metadata-from-context","errorCode":null,"errorMessage":"missing inbound or outbound metadata from context","messagePattern":"missing inbound or outbound metadata from context","errorType":"exception","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"proxy/http/client.go","lineNumber":177,"sourceCode":"\tif err := task.Run(ctx, requestFunc, responseDonePost); err != nil {\n\t\treturn errors.New(\"connection ends\").Base(err)\n\t}\n\n\treturn nil\n}\n\n// fillRequestHeader will fill out the template of the headers\nfunc fillRequestHeader(ctx context.Context, header []*Header) ([]*Header, error) {\n\tif len(header) == 0 {\n\t\treturn header, nil\n\t}\n\n\tinbound := session.InboundFromContext(ctx)\n\toutbounds := session.OutboundsFromContext(ctx)\n\tob := outbounds[len(outbounds)-1]\n\n\tif inbound == nil || ob == nil {\n\t\treturn nil, errors.New(\"missing inbound or outbound metadata from context\")\n\t}\n\n\tdata := struct {\n\t\tSource net.Destination\n\t\tTarget net.Destination\n\t}{\n\t\tSource: inbound.Source,\n\t\tTarget: ob.Target,\n\t}\n\n\tfilled := make([]*Header, len(header))\n\tfor i, h := range header {\n\t\ttmpl, err := template.New(h.Key).Parse(h.Value)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar buf bytes.Buffer\n","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/http/client.go#L159-L195","documentation":"fillRequestHeader needs inbound.Source and the last outbound's Target to template headers (e.g. X-Real-IP style values). If session.InboundFromContext returns nil or the outbounds slice's tail is nil, header rendering cannot proceed and this error is returned (then wrapped as \"failed to fill out header\" by the caller).","triggerScenarios":"settings.headers[] is non-empty AND the session context lacks inbound metadata — e.g. synthetic contexts built by tests or outbounds invoked outside a normal dispatcher flow; or an empty outbounds slice making outbounds[len-1] panic-adjacent/nil.","commonSituations":"Unit tests invoking Process with a bare context; custom dispatchers that strip session metadata; headers configured globally but traffic originating from unusual inbounds.","solutions":["Only configure settings.headers when all traffic through this outbound comes from standard inbounds that attach session data","In tests, inject session.ContextWithInbound/ContextWithOutbounds before calling Process","Remove headers if the upstream proxy does not require them"],"exampleFix":"```go\nctx = session.ContextWithInbound(ctx, &session.Inbound{ Source: src })\nctx = session.ContextWithOutbounds(ctx, &session.Outbound{ Target: dest })\n// then Process\n```","handlingStrategy":"validation","validationCode":"```go\nctx = session.ContextWithInbound(ctx, &session.Inbound{Source: src})\nctx = session.ContextWithOutbounds(ctx, &session.Outbound{Target: target})\n// only now call Process with headers enabled\n```","typeGuard":"```go\nfunc hasSessionMetadata(ctx context.Context) bool {\n    in := session.InboundFromContext(ctx)\n    obs := session.OutboundsFromContext(ctx)\n    return in != nil && len(obs) > 0 && obs[len(obs)-1] != nil\n}\n```","tryCatchPattern":null,"preventionTips":["Inject inbound/outbound context in tests","Avoid headers on outbounds fed by exotic dispatchers","Guard fillRequestHeader callers with metadata checks"],"tags":["http-outbound","context","session","headers","xray"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}