From efc3b5e7d44ce0514a1c2533df98182a7205bf82 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Thu, 4 Dec 2025 18:53:50 +0530 Subject: [PATCH] fix: Handle Instagram API error codes properly in message processing (#13002) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Problem Instagram webhook processing was failing with: > TypeError: no implicit conversion of String into Integer This occurred when handling **echo messages** (outgoing messages) that: * Contained `unsupported_type` attachments, and * Were sent to a recipient user that could **not** be fetched via the Instagram API. In these cases, the webhook job crashed while trying to create or find the contact for the recipient. ### Root Cause The Instagram message service does not correctly handle Instagram API error code **100**: > "Object with ID does not exist, cannot be loaded due to missing permissions, or does not support this operation" When this error occurred during `fetch_instagram_user`: 1. The method fell through to exception tracking without an explicit return. 2. `ChatwootExceptionTracker.capture_exception` returned `true`. 3. As a result, `fetch_instagram_user` effectively returned `true` instead of a hash or empty hash. 4. `ensure_contact` then called `find_or_create_contact(true)` because `true.present?` is `true`. 5. `find_or_create_contact` crashed when it tried to access `true['id']`. So the chain was: ```txt fetch_instagram_user -> returns true ensure_contact -> find_or_create_contact(true) find_or_create_contact -> true['id'] -> TypeError ``` **Example Webhook Payload** ``` { "object": "instagram", "entry": [{ "time": 1764822592663, "id": "17841454414819988", "messaging": [{ "sender": { "id": "17841454414819988" }, // Business account "recipient": { "id": "1170166904857608" }, // User that can't be fetched "timestamp": 1764822591874, "message": { "attachments": [{ "type": "unsupported_type", "payload": { "url": "https://..." } }], "is_echo": true } }] }] } ``` **Corresponding Instagram API error:** ``` { "error": { "message": "The requested user cannot be found.", "type": "IGApiException", "code": 100, "error_subcode": 2534014 } } ``` **Debug Logs (Before Fix)** ``` [InstagramUserFetchError]: Unsupported get request. Object with ID '17841454414819988' does not exist... 100 [DEBUG] result: true [DEBUG] result.present?: true [DEBUG] find_or_create_contact called [DEBUG] user: true [DEBUG] Invalid user parameter - expected hash with id, got TrueClass: true ``` ### Solution ### 1\. Handle Error Code 100 Explicitly We now treat Instagram API error code **100** as a valid case for creating an “unknown” contact, similar to how we already handle error code `9010`: ``` # Handle error code 100: Object doesn't exist or missing permissions # This typically occurs when trying to fetch a user that doesn't exist # or has privacy restrictions. We can safely create an unknown contact. return unknown_user(ig_scope_id) if error_code == 100 ``` This ensures: * `fetch_instagram_user` returns a valid hash for unknown users. * `ensure_contact` can proceed safely without crashing. ### 2\. Prevent Exception Tracker Results from Leaking Through For any **unhandled** error codes, we now explicitly return an empty hash after logging the exception: ``` exception = StandardError.new( "#{error_message} (Code: #{error_code}, IG Scope ID: #{ig_scope_id})" ) ChatwootExceptionTracker.new(exception, account: @inbox.account).capture_exception # Explicitly return empty hash for any unhandled error codes # This prevents the exception tracker result (true/false) from being returned. {} ``` This guarantees that `fetch_instagram_user` always returns either: * A valid user hash, * An “unknown” user hash * An empty hash Fixes https://linear.app/chatwoot/issue/CW-6068/typeerror-no-implicit-conversion-of-string-into-integer-typeerror --- app/services/instagram/message_text.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/services/instagram/message_text.rb b/app/services/instagram/message_text.rb index 3b371a2f4..3bdc569eb 100644 --- a/app/services/instagram/message_text.rb +++ b/app/services/instagram/message_text.rb @@ -59,11 +59,20 @@ class Instagram::MessageText < Instagram::BaseMessageText # We can safely create an unknown contact, making this integration work. return unknown_user(ig_scope_id) if error_code == 9010 + # Handle error code 100: Object doesn't exist or missing permissions + # This typically occurs when trying to fetch a user that doesn't exist or has privacy restrictions + # We can safely create an unknown contact, similar to error 9010 + return unknown_user(ig_scope_id) if error_code == 100 + Rails.logger.warn("[InstagramUserFetchError]: account_id #{@inbox.account_id} inbox_id #{@inbox.id} ig_scope_id #{ig_scope_id}") Rails.logger.warn("[InstagramUserFetchError]: #{error_message} #{error_code}") exception = StandardError.new("#{error_message} (Code: #{error_code}, IG Scope ID: #{ig_scope_id})") ChatwootExceptionTracker.new(exception, account: @inbox.account).capture_exception + + # Explicitly return empty hash for any unhandled error codes + # This prevents the exception tracker result from being returned + {} end def base_uri