Skip to content

Fix empty JID in /session/status after QR pairing#333

Open
AntonKun wants to merge 1 commit into
asternic:mainfrom
AntonKun:jid-fix
Open

Fix empty JID in /session/status after QR pairing#333
AntonKun wants to merge 1 commit into
asternic:mainfrom
AntonKun:jid-fix

Conversation

@AntonKun

Copy link
Copy Markdown
Contributor

Problem

After scanning a QR code, /session/status could report loggedIn: true while returning an empty jid.

Summary

Resolve session JID from the live whatsmeow store in GET /session/status instead of relying only on stale cache/DB values Persist JID to the database and userinfocache on PairSuccess and Connected events.

Solution

  1. resolveSessionJID() in handlers.go
    New helper used by GetStatus to return the best-known JID:
  • If the user is logged in → read from waClient.Store.ID (authoritative source)
  • If the JID is an LID (@lid) → resolve to a phone-number JID via getCachedPNForLID
  • If the resolved JID differs from cache → sync back to userinfocache and users.jid
  • If not logged in and cache is empty → fallback to DB
  1. PairSuccess handler in wmiau.go
    Store JID as an explicit string instead of passing a types.JID struct to SQL
    Prefer waClient.Store.ID over evt.ID when available

  2. Connected handler in wmiau.go
    On successful connection, persist JID from Store.ID to DB and cache
    Covers reconnects and cases where PairSuccess did not update persistence layers

  3. Nil-safe GetStatus
    Guard waClient before calling IsConnected() / IsLoggedIn() to avoid panics when no client exists in memory

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces 'resolveSessionJID' to resolve and cache the best-known JID for a user, updating the status endpoint and event handlers to persist and use this resolved JID. The feedback highlights several critical improvements: ensuring database compatibility with SQLite by using 'db.Rebind' instead of hardcoded PostgreSQL placeholders, propagating the request context to support cancellation, utilizing 'defer cancel()' to prevent context leaks, and adding nil checks to avoid potential panics.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread handlers.go
Comment on lines +770 to +772
if _, err := s.db.Exec("UPDATE users SET jid=$1 WHERE id=$2", resolved, txtid); err != nil {
log.Warn().Err(err).Str("user_id", txtid).Msg("Failed to persist resolved JID")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The hardcoded $1 and $2 placeholders will fail when using SQLite. Use s.db.Rebind with ? placeholders to ensure compatibility with both PostgreSQL and SQLite.

				query := s.db.Rebind("UPDATE users SET jid=? WHERE id=?")
				if _, err := s.db.Exec(query, resolved, txtid); err != nil {
					log.Warn().Err(err).Str("user_id", txtid).Msg("Failed to persist resolved JID")
				}

Comment thread handlers.go
Comment on lines +775 to +779
} else if jid == "" {
var dbJid string
if err := s.db.QueryRow("SELECT jid FROM users WHERE id = $1", txtid).Scan(&dbJid); err == nil && dbJid != "" {
jid = dbJid
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The hardcoded $1 placeholder in the fallback SELECT query will fail on SQLite. Use s.db.Rebind with ? placeholders to maintain database compatibility.

	} else if jid == "" {
		var dbJid string
		query := s.db.Rebind("SELECT jid FROM users WHERE id = ?")
		if err := s.db.QueryRow(query, txtid).Scan(&dbJid); err == nil && dbJid != "" {
			jid = dbJid
		}

Comment thread wmiau.go
Comment on lines +723 to +724
if _, err := mycli.db.Exec(`UPDATE users SET jid=$1 WHERE id=$2`, connectedJID, mycli.userID); err != nil {
log.Warn().Err(err).Str("user_id", mycli.userID).Msg("Failed to persist JID on connect")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The hardcoded $1 and $2 placeholders will fail on SQLite. Use mycli.db.Rebind with ? placeholders to ensure database compatibility.

Suggested change
if _, err := mycli.db.Exec(`UPDATE users SET jid=$1 WHERE id=$2`, connectedJID, mycli.userID); err != nil {
log.Warn().Err(err).Str("user_id", mycli.userID).Msg("Failed to persist JID on connect")
query := mycli.db.Rebind(`UPDATE users SET jid=? WHERE id=?`)
if _, err := mycli.db.Exec(query, connectedJID, mycli.userID); err != nil {
log.Warn().Err(err).Str("user_id", mycli.userID).Msg("Failed to persist JID on connect")

Comment thread wmiau.go
Comment on lines 736 to +737
sqlStmt := `UPDATE users SET jid=$1 WHERE id=$2`
_, err := mycli.db.Exec(sqlStmt, jid, mycli.userID)
_, err := mycli.db.Exec(sqlStmt, jidStr, mycli.userID)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The hardcoded $1 and $2 placeholders in the PairSuccess query will fail on SQLite. Use mycli.db.Rebind with ? placeholders to ensure database compatibility.

Suggested change
sqlStmt := `UPDATE users SET jid=$1 WHERE id=$2`
_, err := mycli.db.Exec(sqlStmt, jid, mycli.userID)
_, err := mycli.db.Exec(sqlStmt, jidStr, mycli.userID)
sqlStmt := mycli.db.Rebind(`UPDATE users SET jid=? WHERE id=?`)
_, err := mycli.db.Exec(sqlStmt, jidStr, mycli.userID)

Comment thread handlers.go
Comment on lines +748 to +756
func (s *server) resolveSessionJID(txtid string, waClient *whatsmeow.Client, userInfo Values) string {
jid := userInfo.Get("Jid")

if waClient != nil && waClient.IsLoggedIn() && waClient.Store != nil && waClient.Store.ID != nil {
storeJID := waClient.Store.ID.ToNonAD()
if storeJID.Server == types.HiddenUserServer {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
pn, err := getCachedPNForLID(ctx, waClient, storeJID)
cancel()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure proper resource cleanup and respect client cancellation, pass the request context (ctx) to resolveSessionJID instead of using context.Background(). Additionally, use defer cancel() immediately after context.WithTimeout to prevent context leaks, and check waClient.Store != nil before calling IsLoggedIn() to avoid potential nil pointer panics.

Suggested change
func (s *server) resolveSessionJID(txtid string, waClient *whatsmeow.Client, userInfo Values) string {
jid := userInfo.Get("Jid")
if waClient != nil && waClient.IsLoggedIn() && waClient.Store != nil && waClient.Store.ID != nil {
storeJID := waClient.Store.ID.ToNonAD()
if storeJID.Server == types.HiddenUserServer {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
pn, err := getCachedPNForLID(ctx, waClient, storeJID)
cancel()
func (s *server) resolveSessionJID(ctx context.Context, txtid string, waClient *whatsmeow.Client, userInfo Values) string {
jid := userInfo.Get("Jid")
if waClient != nil && waClient.Store != nil && waClient.IsLoggedIn() {
storeJID := waClient.Store.ID.ToNonAD()
if storeJID.Server == types.HiddenUserServer {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
pn, err := getCachedPNForLID(ctx, waClient, storeJID)

Comment thread handlers.go
Comment on lines +804 to +807
waClient := clientManager.GetWhatsmeowClient(txtid)
isConnected := waClient != nil && waClient.IsConnected()
isLoggedIn := waClient != nil && waClient.IsLoggedIn()
jid := s.resolveSessionJID(txtid, waClient, userInfo)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Pass the request context r.Context() to resolveSessionJID to support cancellation propagation.

Suggested change
waClient := clientManager.GetWhatsmeowClient(txtid)
isConnected := waClient != nil && waClient.IsConnected()
isLoggedIn := waClient != nil && waClient.IsLoggedIn()
jid := s.resolveSessionJID(txtid, waClient, userInfo)
waClient := clientManager.GetWhatsmeowClient(txtid)
isConnected := waClient != nil && waClient.IsConnected()
isLoggedIn := waClient != nil && waClient.IsLoggedIn()
jid := s.resolveSessionJID(r.Context(), txtid, waClient, userInfo)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant