MHSanaei/3x-ui · warning · ErrClientNotInInbound

%w for email: %s

Error message

%w for email: %s

What it means

Returned when removing a client from an inbound's settings JSON by email: after filtering the settings' clients array, no entry with that email was found, so the code refuses with ErrClientNotInInbound ('... for email: <email>'). The inbound's stored settings do not contain the client the caller expects.

Source

Thrown at internal/web/service/client_inbound_apply.go:936

	var newClients []any
	needApiDel := false
	found := false

	for _, client := range interfaceClients {
		c, ok := client.(map[string]any)
		if !ok {
			continue
		}
		if cEmail, ok := c["email"].(string); ok && cEmail == email {
			found = true
			needApiDel, _ = c["enable"].(bool)
		} else {
			newClients = append(newClients, client)
		}
	}

	if !found {
		return false, fmt.Errorf("%w for email: %s", ErrClientNotInInbound, email)
	}
	db := database.GetDB()
	newClients = compactOrphans(db, newClients)
	if newClients == nil {
		newClients = []any{}
	}
	settings["clients"] = newClients
	newSettings, err := json.MarshalIndent(settings, "", "  ")
	if err != nil {
		return false, err
	}

	prevSettings := oldInbound.Settings
	oldInbound.Settings = string(newSettings)

	emailShared, err := inboundSvc.emailUsedByOtherInbounds(email, inboundId)
	if err != nil {
		return false, err

View on GitHub (pinned to ad32144c42)

Solutions

  1. Callers should treat this error as benign/idempotent — the delete loops already skip it; if you call it directly, do the same with errors.Is(err, ErrClientNotInInbound).
  2. If the client SHOULD be there, inspect the inbound's settings JSON and the client record for email drift (spaces, case, duplicate entries).
  3. Re-save the client on that inbound to re-sync settings JSON with the client table.
  4. Trim/normalize emails on input so storage never accumulates whitespace variants.

Example fix

// before: treating it as a hard failure
err := s.DelInboundClientByEmail(inboundSvc, ibId, email, keepTraffic, true)
if err != nil { return err }

// after: skip the idempotent not-present case
if err != nil && !errors.Is(err, service.ErrClientNotInInbound) {
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// Normalize and check presence before deleting from an inbound
email = strings.ToLower(strings.TrimSpace(email))
if !inboundHasClient(ib, email) {
    return nil // nothing to remove: treat as success
}

Type guard

func IsClientNotInInbound(err error) bool {
    return errors.Is(err, service.ErrClientNotInInbound)
}

Try / catch

if _, err := s.DelInboundClientByEmail(inboundSvc, ibId, email, keepTraffic, true); err != nil {
    if errors.Is(err, service.ErrClientNotInInbound) {
        return false, nil // already absent — idempotent success
    }
    return false, err
}

Prevention

When it happens

Trigger: Calling DelInboundClientByEmail (directly or via the multi-inbound delete loops) with an email that is not present in that inbound's settings.clients — client already removed, email case/whitespace mismatch, or the client only exists in a client-record table but not in this inbound's JSON.

Common situations: Data drift between the clients table and inbound settings JSON (legacy upgrades, hand edits); retried deletes where the first attempt already succeeded; trailing space or different case in the email.

Related errors


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