Skip to content

Conversation

@shubham1g5
Copy link
Contributor

@shubham1g5 shubham1g5 commented Oct 9, 2025

Product Description

https://dimagi.atlassian.net/browse/CCCT-1789

Mostly motivated by this ticket and debugging 400 errors on mobile, currently we see 400 errors in firebase like this without much info on error, this change should add more details to these exceptions.

The second change the PR makes is it corrects the json field from sub_code to error_sub_code as mentioned here

Labels and Review

  • Do we need to enhance the manual QA test coverage ? If yes, the "QA Note" label is set correctly
  • Does the PR introduce any major changes worth communicating ? If yes, the "Release Note" label is set and a "Release Note" is specified in PR description.
  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

cross-request: dimagi/commcare-core#1505

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

📝 Walkthrough

Walkthrough

The PR standardizes API failure handling across the connect stack by changing IApiCallback.processFailure to accept (responseCode, url, errorCode, errorSubCode), removing the InputStream error body. BaseApi now parses error JSON to extract error_code and error_sub_code and logs them via an updated logFailedResponse. ConnectNetworkHelper and all call sites (MessageManager flows and others) are updated to pass the new arguments, defaulting to empty strings when unavailable. BaseApiCallback and PersonalIdApiHandler adapt to the new signature; PersonalIdApiHandler now consumes errorCode/errorSubCode directly and removes local JSON parsing.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Caller (e.g., MessageManager)
  participant A as BaseApi
  participant S as Server
  participant H as IApiCallback implementation<br/>(BaseApiCallback/PersonalIdApiHandler)

  C->>A: executeRequest(url, payload)
  A->>S: HTTP request
  S-->>A: HTTP response

  alt HTTP 2xx
    A->>H: onSuccess(data)
  else HTTP error
    rect rgba(240, 248, 255, 0.6)
    note right of A: New behavior: parse JSON error<br/>fields error_code, error_sub_code
    A->>A: Parse error body (JSON) -> (errorCode, errorSubCode)<br/>fallback: ""
    end
    A->>H: processFailure(responseCode, url, errorCode, errorSubCode)
    H->>H: Handle status-specific logic<br/>(401/403/429/400/5xx, etc.)
  end
Loading
sequenceDiagram
  autonumber
  participant N as ConnectNetworkHelper
  participant H as IApiCallback

  N->>H: processFailure(responseCode, url, "", "")
  note over N,H: Fallback path when no error body available
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • OrangeAndGreen
  • Jignesh-dimagi
  • pm-dimagi

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.94% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description Check ⚠️ Warning The pull request description only includes a Product Description and Labels and Review section, but it does not follow the repository’s required template structure. It is missing the required Technical Summary, Feature Flag, and Safety Assurance sections (including safety story, automated test coverage, and QA plan), and it does not clearly document the design rationale, testing impact, or safety considerations. Because these sections are mandatory for compliance with the template, the description is incomplete. Please update the PR description to include the missing sections from the template. Specifically, add a Technical Summary with the ticket link and design rationale, include a Feature Flag section or clarify that none is required, and provide a Safety Assurance section with a safety story, details on automated test coverage, and a QA plan. This will ensure the description meets repository standards and gives reviewers the necessary context to assess risk and testing.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly identifies a specific and real aspect of the changeset by highlighting improvements to error code handling for Personal ID and logging to Firebase, uses concise language, and avoids vague or generic terms. It accurately reflects part of the change and is clear enough for teammates to understand the core intent.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch logErrorCodes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/org/commcare/connect/network/ConnectNetworkHelper.java (1)

343-377: Fix processFailure argument order

All four calls swap the url, errorCode, and errorSubCode positions, so downstream handlers see blank URLs and receive the endpoint string as the error subcode. That breaks the new logging intent and can mask real error codes. Please pass the arguments in the contract order.

-                        handler.processFailure(responseCode, "", "", url);
+                        handler.processFailure(responseCode, url, "", "");
...
-                handler.processFailure(responseCode, "", "", url);
+                handler.processFailure(responseCode, url, "", "");
...
-                handler.processFailure(responseCode, "", "", url);
+                handler.processFailure(responseCode, url, "", "");
...
-                    handler.processFailure(-1, "", "", url);
+                    handler.processFailure(-1, url, "", "");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9a85f7 and 815ee6e.

📒 Files selected for processing (6)
  • app/src/org/commcare/connect/MessageManager.java (5 hunks)
  • app/src/org/commcare/connect/network/ConnectNetworkHelper.java (4 hunks)
  • app/src/org/commcare/connect/network/IApiCallback.java (1 hunks)
  • app/src/org/commcare/connect/network/base/BaseApi.kt (4 hunks)
  • app/src/org/commcare/connect/network/base/BaseApiCallback.kt (2 hunks)
  • app/src/org/commcare/connect/network/connectId/PersonalIdApiHandler.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
app/src/org/commcare/connect/MessageManager.java (1)
app/src/org/commcare/connect/network/base/BaseApiCallback.kt (1)
  • processFailure (20-53)
app/src/org/commcare/connect/network/connectId/PersonalIdApiHandler.java (2)
app/src/org/commcare/connect/network/base/BaseApiCallback.kt (1)
  • processFailure (20-53)
app/src/org/commcare/connect/network/base/BaseApiHandler.kt (1)
  • onFailure (16-16)
app/src/org/commcare/connect/network/ConnectNetworkHelper.java (1)
app/src/org/commcare/connect/network/base/BaseApiCallback.kt (1)
  • processFailure (20-53)
app/src/org/commcare/connect/network/IApiCallback.java (1)
app/src/org/commcare/connect/network/base/BaseApiCallback.kt (1)
  • processFailure (20-53)

Comment on lines 43 to 66
Logger.exception("Error reading response stream", e);
// Handle error when reading the stream
callback.processFailure(response.code(), null, endPoint)
callback.processFailure(response.code(), "", "", endPoint)
}
} else {
// Handle validation errors
logFailedResponse(response, endPoint)
val stream = if (response.errorBody() != null) response.errorBody()!!
.byteStream() else null
callback.processFailure(response.code(), stream, endPoint)
var errorCode = ""
var errorSubCode = ""
try {
if (stream != null) {
val errorBytes = StreamsUtil.inputStreamToByteArray(stream)
val jsonStr = String(errorBytes, StandardCharsets.UTF_8)
val json = JSONObject(jsonStr)
errorCode = json.optString("error_code", "");
errorSubCode = json.optString("error_sub_code", "");
}
} catch (e: Exception) {
Logger.exception("Error parsing error_code", e);
}
logFailedResponse(response, endPoint, errorCode, errorSubCode)
callback.processFailure(response.code(), errorCode, errorSubCode, endPoint)
}
Copy link

@coderabbitai coderabbitai bot Oct 9, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Ensure processFailure receives (responseCode, url, errorCode, errorSubCode)

Both invocations hand off the parameters in the wrong order (errorCode becomes the URL, etc.), so the callback loses the endpoint context and the parsed error codes. This defeats the PR’s goal of surfacing error codes to Firebase.

-                            callback.processFailure(response.code(), "", "", endPoint)
+                            callback.processFailure(response.code(), endPoint, "", "")
...
-                        callback.processFailure(response.code(), errorCode, errorSubCode, endPoint)
+                        callback.processFailure(response.code(), endPoint, errorCode, errorSubCode)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Logger.exception("Error reading response stream", e);
// Handle error when reading the stream
callback.processFailure(response.code(), null, endPoint)
callback.processFailure(response.code(), "", "", endPoint)
}
} else {
// Handle validation errors
logFailedResponse(response, endPoint)
val stream = if (response.errorBody() != null) response.errorBody()!!
.byteStream() else null
callback.processFailure(response.code(), stream, endPoint)
var errorCode = ""
var errorSubCode = ""
try {
if (stream != null) {
val errorBytes = StreamsUtil.inputStreamToByteArray(stream)
val jsonStr = String(errorBytes, StandardCharsets.UTF_8)
val json = JSONObject(jsonStr)
errorCode = json.optString("error_code", "");
errorSubCode = json.optString("error_sub_code", "");
}
} catch (e: Exception) {
Logger.exception("Error parsing error_code", e);
}
logFailedResponse(response, endPoint, errorCode, errorSubCode)
callback.processFailure(response.code(), errorCode, errorSubCode, endPoint)
}
Logger.exception("Error reading response stream", e);
// Handle error when reading the stream
callback.processFailure(response.code(), endPoint, "", "")
}
} else {
// Handle validation errors
val stream = if (response.errorBody() != null) response.errorBody()!!
.byteStream() else null
var errorCode = ""
var errorSubCode = ""
try {
if (stream != null) {
val errorBytes = StreamsUtil.inputStreamToByteArray(stream)
val jsonStr = String(errorBytes, StandardCharsets.UTF_8)
val json = JSONObject(jsonStr)
errorCode = json.optString("error_code", "");
errorSubCode = json.optString("error_sub_code", "");
}
} catch (e: Exception) {
Logger.exception("Error parsing error_code", e);
}
logFailedResponse(response, endPoint, errorCode, errorSubCode)
callback.processFailure(response.code(), endPoint, errorCode, errorSubCode)
}
🤖 Prompt for AI Agents
In app/src/org/commcare/connect/network/base/BaseApi.kt around lines 43 to 66,
the two callback.processFailure invocations pass parameters in the wrong order
(endpoint is last) — change them to match the required signature (responseCode,
url, errorCode, errorSubCode). Specifically, replace the first call to pass
endPoint as the second argument (response.code(), endPoint, "", "") and the
second call to pass endPoint as the second argument as well (response.code(),
endPoint, errorCode, errorSubCode).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the commit details to see the changes made
gh api repos/dimagi/commcare-android/commits/b5e000c8b76a74e46c2016b3ddebad06c61c0896 --jq '.commit.message'

# Get the file changes in this commit for BaseApi.kt
gh api repos/dimagi/commcare-android/commits/b5e000c8b76a74e46c2016b3ddebad06c61c0896 --jq '.files[] | select(.filename == "app/src/org/commcare/connect/network/base/BaseApi.kt") | .patch'

Length of output: 1020


@shubham1g5, perfect! ✅ The parameter order is now correct in both processFailure calls. The error codes will now be properly passed through to Firebase for better debugging of 400 errors. Thank you for the quick fix!


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Comment on lines 56 to 59
public void processFailure(int responseCode, String url, String errorCode, String errorSubCode) {
if (!handleErrorCodeIfPresent(errorCode, errorSubCode, sessionData)) {
super.processFailure(responseCode, errorCode, errorSubCode, url);
}
Copy link

@coderabbitai coderabbitai bot Oct 9, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Pass params to super.processFailure in new order

Here the errorCode is being sent as the url, and the actual url ends up as the errorSubCode. This prevents BaseApiCallback from logging the endpoint correctly and drops the error code that Firebase is supposed to capture.

-                    super.processFailure(responseCode, errorCode, errorSubCode, url);
+                    super.processFailure(responseCode, url, errorCode, errorSubCode);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void processFailure(int responseCode, String url, String errorCode, String errorSubCode) {
if (!handleErrorCodeIfPresent(errorCode, errorSubCode, sessionData)) {
super.processFailure(responseCode, errorCode, errorSubCode, url);
}
public void processFailure(int responseCode, String url, String errorCode, String errorSubCode) {
if (!handleErrorCodeIfPresent(errorCode, errorSubCode, sessionData)) {
super.processFailure(responseCode, url, errorCode, errorSubCode);
}
}
🤖 Prompt for AI Agents
In app/src/org/commcare/connect/network/connectId/PersonalIdApiHandler.java
around lines 56 to 59, the call to super.processFailure passes parameters in the
wrong order (errorCode is passed as url and errorSubCode as errorCode), causing
incorrect logging and dropped error info; change the call to pass responseCode,
then url, then errorCode, then errorSubCode so it matches the superclass
signature (super.processFailure(responseCode, url, errorCode, errorSubCode)).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the commit details to verify the fix
gh api repos/dimagi/commcare-android/commits/b5e000c8b76a74e46c2016b3ddebad06c61c0896 --jq '.files[] | select(.filename == "app/src/org/commcare/connect/network/connectId/PersonalIdApiHandler.java") | .patch'

Length of output: 532


@shubham1g5 Perfect! ✅ The fix looks great. The parameters are now in the correct order:

super.processFailure(responseCode, url, errorCode, errorSubCode);

This ensures that:

  • The url is correctly logged as the endpoint
  • The errorCode and errorSubCode are properly captured for Firebase reporting

Would you like me to resolve this comment thread?


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@shubham1g5 shubham1g5 removed the skip-integration-tests Skip android tests. label Oct 13, 2025
@shubham1g5
Copy link
Contributor Author

@damagatchi retest this please

else -> "Unexpected Error: $message"
}

errorMessage += " for url ${endPoint ?: "url not found"}"
Copy link
Contributor

Choose a reason for hiding this comment

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

@shubham1g5 Can we make it unknown url as url not found seems like 404 error


@Override
public void processClientError(int responseCode) {
public void processClientError(int responseCode, InputStream errorStream) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why there is change in this method? I cannot see its being used further.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is the same interface used by ConnectNetworkHelper

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's the same interface used by ConnectNetworkHelper

public interface IApiCallback {
void processSuccess(int responseCode, InputStream responseData);
void processFailure(int responseCode, @Nullable InputStream errorResponse, String url);
void processFailure(int responseCode, String url, String errorCode, String errorSubCode);
Copy link
Contributor

Choose a reason for hiding this comment

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

Currently it has requirement of code and sub code but in future it can have multiple codes, just thinking to have Map<String, String> here to support many? Your thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that is plausible for sure, but decided to keep as it is for now and not over-enginner.

Comment on lines 345 to 349
Pair<String, String> errorCodes = NetworkUtils.getErrorCodes(errorStream);
NetworkUtils.logFailedResponse("", responseCode, url, errorCodes.getFirst(),
errorCodes.getSecond());
handler.processFailure(responseCode, url, errorCodes.getFirst(),
errorCodes.getSecond());
Copy link
Contributor

Choose a reason for hiding this comment

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

This code will get removed whenever migration is done to retrofit for remaining methods in ApiPersonalId.java. processClientError(int responseCode, InputStream errorStream) is un necessarily getting changed here and causing changes in other files like ModernHttpTask.java. If we ignore the functionality for connect messages (which doesn't have sub error code right now) and only focus on personalId sign up, I think these changes are not required. Connect message will be automatically covered for sub error code whenever retrofit migration is done in ApiPersonalId.java.

@shubham1g5 shubham1g5 added this to the 2.61 milestone Oct 14, 2025
@shubham1g5
Copy link
Contributor Author

shubham1g5 commented Oct 14, 2025

@Jignesh-dimagi I am no longer planning these changes for 2.60 given QA has already completed and there is some more thought I need to give to this PR around logging right things. I have instead separated out the most critical change from this PR here instead that would be nice to get in 2.60

@shubham1g5 shubham1g5 merged commit d887a1b into commcare_2.60 Oct 16, 2025
2 checks passed
@shubham1g5 shubham1g5 deleted the logErrorCodes branch October 16, 2025 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants