MHSanaei/3x-ui · warning

token not found

Error message

token not found

What it means

ApiTokenService.SetEnabled updates the api_tokens row and distinguishes 'no such token' (RowsAffected == 0) from a real DB error. The id either never existed or was already deleted; note GORM's Update reports 0 affected rows for both a missing id and an update that writes the same value depending on dialect — here the guard runs after a clean Update, so a missing/deleted row is the cause. id<=0 is rejected separately as 'invalid token id'.

Source

Thrown at internal/web/service/panel/api_token.go:131

func (s *ApiTokenService) Delete(id int) error {
	if id <= 0 {
		return common.NewError("invalid token id")
	}
	db := database.GetDB()
	return db.Where("id = ?", id).Delete(model.ApiToken{}).Error
}

func (s *ApiTokenService) SetEnabled(id int, enabled bool) error {
	if id <= 0 {
		return common.NewError("invalid token id")
	}
	db := database.GetDB()
	res := db.Model(model.ApiToken{}).Where("id = ?", id).Update("enabled", enabled)
	if res.Error != nil {
		return res.Error
	}
	if res.RowsAffected == 0 {
		return errors.New("token not found")
	}
	return nil
}

// Match returns true when the presented bearer token matches any enabled
// row in api_tokens. Tokens are stored as SHA-256 hashes, so the presented
// value is hashed before a constant-time compare per row keeps a remote
// attacker from timing the comparison byte-by-byte.
func (s *ApiTokenService) Match(presented string) bool {
	if presented == "" {
		return false
	}
	db := database.GetDB()
	var rows []*model.ApiToken
	if err := db.Model(model.ApiToken{}).Where("enabled = ?", true).Find(&rows).Error; err != nil {
		return false
	}
	presentedHash := []byte(crypto.HashTokenSHA256(presented))

View on GitHub (pinned to ad32144c42)

Solutions

  1. Refresh the token list and retry against an id that still exists
  2. If the token was intentionally removed, treat this as success in the UI (idempotent delete-then-toggle)
  3. Confirm the id being sent matches the row in api_tokens
Defensive patterns

Strategy: validation

Validate before calling

if id <= 0 {
    return errors.New("invalid token id")
}
exists, _ := tokenService.Exists(id)
if !exists { return nil } // treat as already deleted

Try / catch

if err := tokenService.SetEnabled(id, enabled); err != nil {
    if errors.Is(err, ErrTokenNotFound) || strings.Contains(err.Error(), "token not found") {
        return nil // idempotent: row already gone
    }
    return err
}

Prevention

When it happens

Trigger: Toggling enable/disable on a token row that another admin/tab already deleted; stale UI list after token rotation; passing an id of 0 from an unselected list row (that yields 'invalid token id' instead).

Common situations: Two admins managing API tokens concurrently; frontend cache showing removed tokens.

Related errors


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