v2fly/v2ray-core · error
no inbound metadata
Error message
no inbound metadata
What it means
This is a hard Go panic raised in the VMess inbound's Process() after the VMess request header is decoded and logged. The handler assumes the context carries session.Inbound metadata, which is attached exclusively by the proxyman inbound worker (app/proxyman/inbound/worker.go via session.ContextWithInbound) when the connection is accepted by a registered inbound handler. session.InboundFromContext(ctx) returning nil proves the proxy ran outside that pipeline, so it panics rather than dispatching traffic with no accounting/metadata.
Source
Thrown at proxy/vmess/inbound/inbound.go:275
if request.Command != protocol.RequestCommandMux {
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
From: connection.RemoteAddr(),
To: request.Destination(),
Status: log.AccessAccepted,
Reason: "",
Email: request.User.Email,
})
}
newError("received request for ", request.Destination()).WriteToLog(session.ExportIDToError(ctx))
if err := connection.SetReadDeadline(time.Time{}); err != nil {
newError("unable to set back read deadline").Base(err).WriteToLog(session.ExportIDToError(ctx))
}
inbound := session.InboundFromContext(ctx)
if inbound == nil {
panic("no inbound metadata")
}
inbound.User = request.User
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
link, err := dispatcher.Dispatch(ctx, request.Destination())
if err != nil {
return newError("failed to dispatch request to ", request.Destination()).Base(err)
}
requestDone := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
bodyReader, err := svrSession.DecodeRequestBody(request, reader)View on GitHub (pinned to db12914161)
Solutions
- Route connections through a registered inbound handler so proxyman's worker attaches the Inbound before the proxy runs.
- In custom entry points/tests, set the metadata explicitly: ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: "vmess-in"}) prior to Process().
- Grep custom code paths between accept and Process for context shadowing or reassignment; the ctx reaching Process must be a descendant of the one the worker built.
- Mirror the working patterns: app/tun/handler_tcp.go:89, app/reverse/bridge.go:105, app/proxyman/inbound/worker.go:83.
- Wrap risky new paths in recover() during development so the panic names the missing wiring instead of crashing the whole node.
Example fix
// before
func (h *customHandler) handle(conn net.Conn) {
ctx := context.Background()
go h.vmessInbound.Process(ctx, link, conn) // panic: no inbound metadata
}
// after
func (h *customHandler) handle(conn net.Conn) {
ctx := session.ContextWithInbound(context.Background(), &session.Inbound{
Tag: h.tag,
Source: net.DestinationFromAddr(conn.RemoteAddr()),
})
go h.vmessInbound.Process(ctx, link, conn)
} Defensive patterns
Strategy: validation
Validate before calling
// guard before dispatching into the VMess inbound:
if session.InboundFromContext(ctx) == nil {
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Tag: "vmess-in",
Source: net.DestinationFromAddr(conn.RemoteAddr()),
})
}
go vmessInbound.Process(ctx, link, conn) Type guard
func hasInboundMetadata(ctx context.Context) bool {
return session.InboundFromContext(ctx) != nil
} Try / catch
func (h *Host) dispatchVMess(ctx context.Context, c net.Conn) {
defer func() {
if r := recover(); r != nil {
h.log.Error("vmess inbound panic: missing session.Inbound in context?", "panic", r)
}
}()
_ = h.vmessInbound.Process(ctx, h.link, c)
} Prevention
- Never call inbound proxy handlers with contexts that skipped the proxyman worker; that worker is what attaches session.Inbound.
- Custom listeners must replicate session.ContextWithInbound (see app/tun/handler_tcp.go:89, app/reverse/bridge.go:105).
- Watch for ctx shadowing (:=) in helper functions on the accept-to-Process path.
- Add an integration test that runs one full connection through your entry point; it will panic immediately if inbound metadata is missing.
When it happens
Trigger: Directly invoking the VMess inbound handler with a hand-built context (tests, benchmarks, custom servers); a custom accept loop or transport that calls proxy.Process without first calling session.ContextWithInbound; middleware between the listener and the proxy that replaces the context (e.g. wraps it in a struct not delegating Value, or assigns a new context); integrating the proxy behind an API/websocket shim that spawns its own goroutines with fresh contexts.
Common situations: Unit tests for the VMess decoder that drive the full handler with context.Background(); porting the inbound into a standalone service; adding transports like TUN/socket-activation without replicating app/tun/handler_tcp.go's ContextWithInbound call; bugs where ctx is shadowed (ctx := ...) inside a helper before Process is reached.
Related errors
AI-assisted analysis of v2fly/v2ray-core@db12914161 (2026-08-15).
Data as JSON: /api/errors/57c900de3c39f773.
Report an issue: GitHub.