MHSanaei/3x-ui · error

remote inbound with tag %q not found on node %s

Error message

remote inbound with tag %q not found on node %s

What it means

Thrown by Remote.resolveRemoteID when an inbound tag known centrally has no counterpart on the node, even after a fresh refreshRemoteIDs() pull of the node's inbound list. The runtime needs the node-local numeric inbound ID to address per-client RPCs (add/detach), and tag-to-ID resolution failed. It almost always means central and node disagree about which inbounds exist.

Source

Thrown at internal/web/runtime/remote.go:304

		return nil, fmt.Errorf("decode envelope: %w", err)
	}
	if !env.Success {
		return &env, &remoteAPIError{msg: env.Msg}
	}
	return &env, nil
}

func (r *Remote) resolveRemoteID(ctx context.Context, tag string) (int, error) {
	if id, ok := r.cacheGetTag(tag); ok {
		return id, nil
	}
	if err := r.refreshRemoteIDs(ctx); err != nil {
		return 0, err
	}
	if id, ok := r.cacheGetTag(tag); ok {
		return id, nil
	}
	return 0, fmt.Errorf("remote inbound with tag %q not found on node %s", tag, r.node.Name)
}

// nodeInboundTagPrefix is the central-panel alias for an inbound on nodeID.
// Kept in sync with service.nodeTagPrefix (port_conflict.go); duplicated here
// so runtime does not import service.
func nodeInboundTagPrefix(nodeID int) string {
	return fmt.Sprintf("n%d-", nodeID)
}

// stripNodeInboundTagPrefix removes the central-only n<id>- prefix before
// pushing an inbound to the node so Xray keeps its original tag and routing.
func stripNodeInboundTagPrefix(nodeID int, tag string) string {
	if stripped, ok := strings.CutPrefix(tag, nodeInboundTagPrefix(nodeID)); ok {
		return stripped
	}
	return tag
}

View on GitHub (pinned to ad32144c42)

Solutions

  1. Open the node's own panel and check whether the inbound (with the un-prefixed tag) exists; recreate it if it was deleted out-of-band.
  2. Trigger a full inbound push/reconcile from the master (save the inbound or run ReconcileNode) so the node gets the missing inbound before per-client ops run.
  3. Stop editing node-side inbounds directly — manage node-assigned inbounds only through the central panel.
  4. If tags drifted, rename on the node back to what the central settings JSON expects.

Example fix

// before: inbound "n3-vless-443" exists centrally but node has "vless-8443" (renamed locally)
// err: remote inbound with tag "n3-vless-443" not found on node edge-1

// after: on the node, rename the inbound back to vless-443 (or re-save the inbound on the master to force a fresh push) -> resolveRemoteID succeeds
Defensive patterns

Strategy: validation

Validate before calling

// Verify tag parity before per-client writes
ids, err := remote.ListInboundTags(ctx)
if err != nil { return err }
if !slices.Contains(ids, stripNodeInboundTagPrefix(nodeID, ib.Tag)) {
    return fmt.Errorf("tag %s missing on node; push the inbound first", ib.Tag)
}

Type guard

func isTagNotFound(err error) bool {
    return err != nil && strings.Contains(err.Error(), "not found on node")
}

Try / catch

if err := remote.AddClient(ctx, ib, client); err != nil {
    if isTagNotFound(err) {
        return pushInboundThenRetry(ctx, ib, client) // converge then retry once
    }
    return err
}

Prevention

When it happens

Trigger: AddClient/DeleteUser for an inbound whose tag is absent on the node: the inbound was deleted or renamed directly on the node, the node DB was reset/re-imported, an out-of-band Xray config edit changed tags, or a partial earlier push never created the inbound on the node.

Common situations: Someone edited inbounds on the node's own panel instead of the master; node restored from an old backup; central tag n<id>- prefix stripped inconsistently; failed initial push left central thinking the inbound exists remotely.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/e2f221872d08212f. Report an issue: GitHub.