fix(user): resolve /user/avatar info query timeout via canonical JID#120
fix(user): resolve /user/avatar info query timeout via canonical JID#120cesar-carlos wants to merge 3 commits into
Conversation
CreateJID/ParseJID prefixes phone numbers with "+", which breaks the raw GetProfilePictureInfo IQ (Target=jid) and surfaces as "info query timed out" after ~75s on POST /user/avatar. Strip the prefix via CanonicalJID and ToNonAD before the request, matching the existing fix for reactions/typing/receipts. Closes evolution-foundation#76 Co-authored-by: Cursor <cursoragent@cursor.com>
Reviewer's guide (collapsed on small PRs)Reviewer's GuideCanonicalizes user JIDs before requesting profile picture info to avoid IQ timeouts and improves error logging for avatar retrieval failures. Sequence diagram for canonical JID in avatar lookupsequenceDiagram
actor User
participant ApiServer
participant userService
participant utils
participant whatsappClient
User->>ApiServer: POST /user/avatar
ApiServer->>userService: GetAvatar(data, instance)
userService->>userService: ParseJID(data.Phone)
userService->>utils: CanonicalJID(jid)
utils-->>userService: canonicalJID
userService->>userService: ToNonAD(canonicalJID)
userService->>whatsappClient: GetProfilePictureInfo(jid, preview)
alt IQ succeeds
whatsappClient-->>userService: pic
userService-->>ApiServer: avatar URL
ApiServer-->>User: 200 OK
else IQ times out
whatsappClient-->>userService: ErrIQTimedOut
userService->>userService: fmt.Errorf("get profile picture for jid: %w", ErrIQTimedOut)
userService-->>ApiServer: error
ApiServer-->>User: 500 info query timed out
end
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="pkg/user/service/user_service.go" line_range="357-358" />
<code_context>
if err != nil {
u.loggerWrapper.GetLogger(instance.Id).LogError("[%s] GetProfilePictureInfo failed: %v", instance.Id, err)
- return nil, err
+ return nil, fmt.Errorf("get profile picture for %s: %w", jid, err)
}
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Revisit including the JID in the wrapped error for potential PII exposure.
Embedding the JID in the returned error introduces user-identifying data that may surface in logs or external error messages. Consider omitting or masking the JID in the error text, and if you need it for debugging, log it separately at an appropriate level while keeping the returned error generic.
```suggestion
u.loggerWrapper.GetLogger(instance.Id).LogError("[%s] GetProfilePictureInfo failed: %v", instance.Id, err)
return nil, fmt.Errorf("get profile picture: %w", err)
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| u.loggerWrapper.GetLogger(instance.Id).LogError("[%s] GetProfilePictureInfo failed: %v", instance.Id, err) | ||
| return nil, err | ||
| return nil, fmt.Errorf("get profile picture for %s: %w", jid, err) |
There was a problem hiding this comment.
🚨 suggestion (security): Revisit including the JID in the wrapped error for potential PII exposure.
Embedding the JID in the returned error introduces user-identifying data that may surface in logs or external error messages. Consider omitting or masking the JID in the error text, and if you need it for debugging, log it separately at an appropriate level while keeping the returned error generic.
| u.loggerWrapper.GetLogger(instance.Id).LogError("[%s] GetProfilePictureInfo failed: %v", instance.Id, err) | |
| return nil, err | |
| return nil, fmt.Errorf("get profile picture for %s: %w", jid, err) | |
| u.loggerWrapper.GetLogger(instance.Id).LogError("[%s] GetProfilePictureInfo failed: %v", instance.Id, err) | |
| return nil, fmt.Errorf("get profile picture: %w", err) |
Canonical JID fix for /user/avatar timeout (PR evolution-foundation#120).
|
Closing from the author side: these changes are already merged into our fork The commits remain available on the PR branch if maintainers still want to review/merge into |
|
Reopened — keeping this PR available for upstream review/merge. |
Cap GetProfilePictureInfo at 8s using the request context so clients with ~12s read timeouts get HTTP 504 instead of a hung connection. Map WhatsApp rate-overlimit to HTTP 429. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Updated after production avatar failure report (Chatwoot account enrichment):
Please re-review the new commit on this branch. |
Prefer PN JID when the LID store has a mapping before the profile picture IQ. Replace fixed Sleep(2s) after StartInstance with a context-aware ready poll so avatar timeouts are not wasted on wait. Map context.Canceled to HTTP 504 like other request aborts. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Follow-up improvements from review:
|
Description
POST /user/avatarhangs ~75s and returns HTTP 500 with"info query timed out"even when the instance is connected and message send/receive works.Root cause (original)
CreateJID/ParseJIDbuild phone JIDs with a leading+(e.g.+5566...@s.whatsapp.net).GetProfilePictureInfosends a raw IQ withTarget: jid. WhatsApp does not accept that+on this path, so the IQ never gets a response and whatsmeow surfacesErrIQTimedOut(info query timed out).This is the same class of bug already fixed for reactions / typing / read receipts via
utils.CanonicalJID.What changed
GetAvatar, afterParseJID:jid = utils.CanonicalJID(jid).ToNonAD()beforeGetProfilePictureInfo.c.Request.Context(), mapErrIQRateOverLimit→ HTTP 429, map IQ/context timeout → HTTP 504. Clients with ~12s read timeouts no longer hang on a ~80s server wait.Before / after
POST /user/avatar(+ JID)"info query timed out"~75surlin ~0.1–2sRelated Issue
Closes #76
Type of Change
Testing
writeUserWAError)Checklist
Additional Notes
Follow-up PR #121 adds
PictureURLonPOST /user/info, LID→PN resolve, and docs for canonical query order.