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
- Refresh the token list and retry against an id that still exists
- If the token was intentionally removed, treat this as success in the UI (idempotent delete-then-toggle)
- 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
- Refresh the token list before toggling after concurrent admin changes
- Treat toggle-after-delete as success in UI handlers
- Send the id from the freshly loaded row, never a cached one
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
- The new username and password are empty
- tg_id must be a positive integer
- invalid metric
- invalid bucket
- invalid bucket
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/a73299aeb7285877.
Report an issue: GitHub.