m1k1o/neko · error
session token already exists
Error message
session token already exists
What it means
Create in server/internal/session/manager.go throws this ad-hoc error when the generated (or supplied) session token is already present in manager.tokens. Sessions and their login tokens are stored in two maps; while the session ID may be free, the token must also be unique. The manager returns (nil, "") and closes over this condition to prevent two sessions sharing one token, which would let one token authenticate to multiple sessions.
Source
Thrown at server/internal/session/manager.go:106
totalUsers atomic.Int32
lastUserLeftAt atomic.Value
}
func (manager *SessionManagerCtx) Create(id string, profile types.MemberProfile) (types.Session, string, error) {
token, err := utils.NewUID(64)
if err != nil {
return nil, "", err
}
manager.sessionsMu.Lock()
if _, ok := manager.sessions[id]; ok {
manager.sessionsMu.Unlock()
return nil, "", types.ErrSessionAlreadyExists
}
if _, ok := manager.tokens[token]; ok {
manager.sessionsMu.Unlock()
return nil, "", errors.New("session token already exists")
}
session := &SessionCtx{
id: id,
token: token,
manager: manager,
logger: manager.logger.With().Str("session_id", id).Logger(),
profile: profile,
}
manager.tokens[token] = id
manager.sessions[id] = session
manager.sessionsMu.Unlock()
manager.emmiter.Emit("created", session)
manager.save()
return session, token, nilView on GitHub (pinned to b0f01cedea)
Solutions
- Let the manager generate a random token instead of passing a fixed one to Create.
- Ensure the old session is destroyed (Destroy/logout) before reusing its token.
- If migrating state, rebuild the tokens map consistently with the sessions map so no stale token entries remain.
- Handle the returned error by retrying Create with a freshly generated token.
Example fix
// before
session, token, err := manager.Create("user-42", "static-token", profile)
// after
token, err := randomToken() // e.g. crypto/rand generated
if err != nil { return err }
session, token, err := manager.Create("user-42", token, profile) Defensive patterns
Strategy: retry
Validate before calling
manager.sessionsMu.Lock()
_, idTaken := manager.sessions[id]
_, tokTaken := manager.tokens[token]
manager.sessionsMu.Unlock()
if tokTaken {
token = generateNewRandomToken() // regenerate before calling Create
} Try / catch
session, tok, err := manager.Create(id, token, profile)
if err != nil && err.Error() == "session token already exists" {
return manager.Create(id, generateNewRandomToken(), profile) // retry once with fresh token
} Prevention
- Always let the manager generate tokens with crypto/rand instead of supplying fixed tokens.
- Destroy old sessions before reusing their tokens.
- Never derive tokens from user IDs or predictable values.
- In tests, use unique tokens per session creation.
When it happens
Trigger: Calling sessionMgr.Create with a token string that an existing live session already holds (e.g. manually supplying a fixed/derived token in Create's arguments instead of letting the manager generate a random one, or after restoring/duplicating sessions without clearing the tokens map).
Common situations: Developers wiring custom token generation (e.g. deriving tokens from a user ID or a static config value) instead of the default random token; tests that create two sessions with the same hardcoded token; restart/reconnect logic that reuses a previously issued token while the old session still exists in the manager.
Related errors
- session not found
- session already exists
- session is already connected
- session login disabled
- session logins locked
AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01).
Data as JSON: /api/errors/f184d1467ebd82c9.
Report an issue: GitHub.