Skip to content

Conversation

@OrangeAndGreen
Copy link
Contributor

Cleaned up some of the error handling code around API errors from PersonalID calls.

  • Passing through errors from JSONExceptions so they can be custom-handled or otherwise handled by the default error handler (which crashes the app). The PersonalID calls all crash on JSON errors, but this pattern will be used again for the Connect API calls where some calls are able to survive individual JSONExceptions.
  • For common network errors that allow retry (bad network, expired token, etc.), error messages are now displayed on the UI instead of via toast messages.
  • Crashing for unknown API errors (instead of a generic toast message)
  • Custom handler for FORBIDDEN (403) response at start_configuration

Crashing on JSONExceptions or unknown errors by default
No generic toast message about not being able to reach server.
…ents can display in UI (instead of toast messages).
@coderabbitai
Copy link

coderabbitai bot commented Jun 6, 2025

📝 Walkthrough

Walkthrough

This change refactors error handling and display logic across several fragments and related classes in the Personal ID feature. It updates error handler method signatures to accept a Throwable parameter, returns error messages as strings instead of directly displaying UI elements, and centralizes error message display in fragment-level methods. The PersonalIdApiHandler and its error code enum are updated to distinguish error types more precisely, including adding a FORBIDDEN_ERROR. Several UI fragments now clear previous error messages before new API calls and display error messages using new helper methods. Some method and resource names are corrected or clarified.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Fragment
    participant PersonalIdApiHandler
    participant ApiPersonalId
    participant PersonalIdApiErrorHandler

    User->>Fragment: Initiate API action (e.g., confirm backup code)
    Fragment->>Fragment: clearError()
    Fragment->>PersonalIdApiHandler: make API call
    PersonalIdApiHandler->>ApiPersonalId: callApi()
    ApiPersonalId-->>PersonalIdApiHandler: Callback (success or failure)
    alt Success
        PersonalIdApiHandler-->>Fragment: onSuccess()
    else Failure
        PersonalIdApiHandler-->>Fragment: onFailure(errorCode, throwable)
        Fragment->>PersonalIdApiErrorHandler: handle(errorCode, throwable)
        PersonalIdApiErrorHandler-->>Fragment: return error message
        Fragment->>Fragment: showError(error message)
    end
Loading

Possibly related PRs

Suggested labels

skip-integration-tests, High Risk

Suggested reviewers

  • pm-dimagi
  • Jignesh-dimagi
  • shubham1g5
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🔭 Outside diff range comments (1)
app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java (1)

122-134: 🛠️ Refactor suggestion

Consider adding error clearing before API call for consistency.

Unlike other fragments in this refactor, there's no clearOtpError() call before the validateFirebaseIdToken() API call. This could leave stale error messages visible if a previous error occurred.

Add error clearing at the start of the method:

 private void validateFirebaseIdToken(String firebaseIdToken) {
+    clearOtpError();
     
     new PersonalIdApiHandler() {
🧹 Nitpick comments (1)
app/src/org/commcare/connect/network/PersonalIdApiHandler.java (1)

63-87: Consider removing the toast message for consistency.

The PR objectives mention replacing toast messages with UI error displays. However, line 74-75 still shows a toast for server error messages.

Consider removing the toast to maintain consistency with the new error handling approach, or document why this specific toast should remain.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 443f9c3 and feec0ef.

📒 Files selected for processing (11)
  • app/res/layout/screen_personalid_phoneno.xml (1 hunks)
  • app/res/values/strings.xml (0 hunks)
  • app/src/org/commcare/connect/PersonalIdManager.java (1 hunks)
  • app/src/org/commcare/connect/network/ApiPersonalId.java (1 hunks)
  • app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java (1 hunks)
  • app/src/org/commcare/connect/network/PersonalIdApiHandler.java (2 hunks)
  • app/src/org/commcare/fragments/personalId/PersonalIdBackupCodeFragment.java (3 hunks)
  • app/src/org/commcare/fragments/personalId/PersonalIdNameFragment.java (3 hunks)
  • app/src/org/commcare/fragments/personalId/PersonalIdPhoneFragment.java (4 hunks)
  • app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java (2 hunks)
  • app/src/org/commcare/fragments/personalId/PersonalIdPhotoCaptureFragment.java (2 hunks)
💤 Files with no reviewable changes (1)
  • app/res/values/strings.xml
🧰 Additional context used
🧬 Code Graph Analysis (5)
app/src/org/commcare/fragments/personalId/PersonalIdBackupCodeFragment.java (1)
app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java (1)
  • PersonalIdApiErrorHandler (21-47)
app/src/org/commcare/fragments/personalId/PersonalIdNameFragment.java (1)
app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java (1)
  • PersonalIdApiErrorHandler (21-47)
app/src/org/commcare/fragments/personalId/PersonalIdPhotoCaptureFragment.java (1)
app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java (1)
  • PersonalIdApiErrorHandler (21-47)
app/src/org/commcare/fragments/personalId/PersonalIdPhoneFragment.java (1)
app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java (1)
  • PersonalIdApiErrorHandler (21-47)
app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java (1)
app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java (1)
  • PersonalIdApiErrorHandler (21-47)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Lint Code Base
🔇 Additional comments (27)
app/res/layout/screen_personalid_phoneno.xml (1)

119-119: LGTM! Improved ID naming consistency.

The TextView ID change from personalid_name_error to personalid_phone_error correctly aligns the element identifier with its purpose in the phone number input screen.

app/src/org/commcare/connect/PersonalIdManager.java (1)

500-502: Excellent resource management improvement.

The try-with-resources statement ensures the InputStream is properly closed after use, preventing potential resource leaks. This is a best practice that improves the robustness of the code.

app/src/org/commcare/connect/network/ApiPersonalId.java (1)

244-244: Good addition of null-safety annotations.

The @NonNull annotations on the onFailure method parameters improve type safety and make the non-null contract explicit, helping prevent potential null pointer exceptions.

app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java (3)

31-32: Good architectural improvement with method signature change.

The refactored method signature that returns error message strings and accepts a Throwable parameter is a significant improvement that separates error message generation from UI handling, enabling better error context and more flexible error display.


35-42: Well-implemented error message generation.

Returning localized error message strings instead of directly triggering UI side effects is a good architectural pattern that gives callers more control over error presentation.


40-40:

Details

✅ Verification successful

Verify null return handling for TOKEN_DENIED_ERROR.

The method returns null for TOKEN_DENIED_ERROR case. Ensure that all calling code properly handles this null return value to avoid potential null pointer exceptions.


🏁 Script executed:

#!/bin/bash
# Search for usages of PersonalIdApiErrorHandler.handle to verify null handling
rg -A 5 -B 2 "PersonalIdApiErrorHandler\.handle"

Length of output: 5023


🏁 Script executed:

#!/bin/bash
# Show the handle method(s) in PersonalIdApiErrorHandler to inspect return types and null handling
rg -n "handle(" -C5 app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java

Length of output: 154


🏁 Script executed:

#!/bin/bash
# Extract the handle method implementation to verify return values, including null
rg -n 'public static .*handle\(.*' -C20 app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java

Length of output: 1863


🏁 Script executed:

#!/bin/bash
# Locate definitions of showError and displayOtpError to verify null handling
rg -n "void showError" -C5 app/src
rg -n "void displayOtpError" -C5 app/src

Length of output: 7483


No changes needed: null return is handled safely
The only null return occurs on TOKEN_DENIED_ERROR, and all call sites pass that null into UI methods (showError/displayOtpError), which simply do TextView.setText(null). Android’s setText(null) is a no-op (renders an empty string) and does not throw NPEs.

app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java (2)

130-132: LGTM: Error handling signature and delegation updated correctly.

The onFailure method signature is properly updated to accept the Throwable parameter and correctly delegates to handleFailure with both the error code and throwable context.


309-311: LGTM: Centralized error handling implementation.

The handleFailure method correctly uses the updated PersonalIdApiErrorHandler.handle() method that returns a localized error message string, then displays it using the existing displayOtpError() method.

app/src/org/commcare/fragments/personalId/PersonalIdBackupCodeFragment.java (3)

171-171: LGTM: Error clearing added before API call.

The clearError() call at the start of confirmBackupCode() ensures any previous error messages are cleared before initiating the new API request, following the consistent pattern established in this refactor.


190-196: LGTM: Updated error handling with Throwable context.

The onFailure method signature is correctly updated to accept the Throwable parameter, and the error is properly displayed using the centralized PersonalIdApiErrorHandler.handle() method that returns a localized error message.


213-221: LGTM: Consistent error UI management methods.

The clearError() and showError() methods follow the same pattern as other fragments in this refactor, properly managing the visibility and content of the error message UI element.

app/src/org/commcare/fragments/personalId/PersonalIdPhotoCaptureFragment.java (3)

85-85: LGTM: Error clearing added before API call.

The clearError() call ensures any previous error messages are cleared before starting the profile completion process, maintaining consistency with the error handling refactor.


95-97: LGTM: Updated failure callback signature.

The onFailure method signature is correctly updated to accept the Throwable parameter and properly delegates to the onCompleteProfileFailure method with both the error code and throwable context.


103-110: LGTM: Enhanced error handling with retry logic.

The onCompleteProfileFailure method correctly uses the updated PersonalIdApiErrorHandler.handle() method to get a localized error message and displays it using the existing showError() method. The retry logic appropriately re-enables buttons when the error code allows retry.

app/src/org/commcare/fragments/personalId/PersonalIdNameFragment.java (4)

78-78: LGTM: Error clearing added before API call.

The clearError() call at the start of verifyOrAddName() ensures any previous error messages are cleared before initiating the name verification process, following the consistent pattern from this refactor.


87-89: LGTM: Updated failure callback signature and delegation.

The onFailure method signature is correctly updated to accept the Throwable parameter and properly delegates to navigateFailure with both the error code and throwable context.


97-103: LGTM: Enhanced error handling with retry logic.

The navigateFailure method correctly uses the updated PersonalIdApiErrorHandler.handle() method to get a localized error message and displays it using showError(). The retry logic appropriately re-enables the continue button when the error code allows retry.


105-113: LGTM: Consistent error UI management methods.

The clearError() and showError() methods follow the same pattern as other fragments in this refactor, properly managing the visibility and content of the error message UI element.

app/src/org/commcare/fragments/personalId/PersonalIdPhoneFragment.java (4)

179-179: Good practice to clear previous errors.

Clearing error messages before starting a new request ensures users don't see stale error messages from previous attempts.


209-209: Typo fix improves code consistency.


219-224: Improved error handling with specific treatment for FORBIDDEN_ERROR.

The differentiated handling for FORBIDDEN_ERROR during configuration aligns well with the PR objectives.


248-256: Well-structured error display methods.

The centralized error handling methods improve code maintainability and consistency.

app/src/org/commcare/connect/network/PersonalIdApiHandler.java (5)

30-37: Appropriate error categorization and retry logic.

The addition of FORBIDDEN_ERROR and the refined retry logic correctly distinguish between transient errors (network issues) and permanent errors (parsing/validation failures).


42-42: Simplified callback creation improves clarity.


50-56: Enhanced error handling with specific exception types.

Separating JSON parsing errors from IO errors provides better debugging context.


92-108: Consistent error handling across all failure scenarios.


149-149: Enhanced error context propagation.

Adding the Throwable parameter enables better debugging and more informative error messages.

Comment on lines +43 to 45
default:
throw new RuntimeException(t);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider safer default error handling.

The default case throws a RuntimeException which will crash the app. Consider returning a generic error message string instead to maintain app stability while still logging the unexpected error.

-default:
-    throw new RuntimeException(t);
+default:
+    if (t != null) {
+        Logger.exception("Unhandled API error", t);
+    }
+    return activity.getString(R.string.generic_api_error);
📝 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
default:
throw new RuntimeException(t);
}
default:
if (t != null) {
Logger.exception("Unhandled API error", t);
}
return activity.getString(R.string.generic_api_error);
}
🤖 Prompt for AI Agents
In app/src/org/commcare/connect/network/PersonalIdApiErrorHandler.java around
lines 43 to 45, the default case throws a RuntimeException which can crash the
app. Modify this to catch the unexpected error safely by logging the error and
returning a generic error message string instead, ensuring the app remains
stable without abrupt termination.

@OrangeAndGreen OrangeAndGreen changed the base branch from master to commcare_2.57 June 9, 2025 13:36
Copy link
Contributor

@shubham1g5 shubham1g5 left a comment

Choose a reason for hiding this comment

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

Good set of changes overall, left a few more suggestions.

Comment on lines +77 to 85
} catch (JSONException e) {
Logger.exception("JSON error parsing API error response", e);
onFailure(PersonalIdApiErrorCodes.JSON_PARSING_ERROR, e);
return;
} catch (IOException e) {
Logger.exception("Error parsing API error response", e);
onFailure(defaultFailureCode);
onFailure(PersonalIdApiErrorCodes.NETWORK_ERROR, e);
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Error in response handling of error body should not propogate same as error in response hadling of successful response body, think it's fine to eat these errors instead and have the onFailure be called with an "unknown error" message instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure I understand. Are you saying to create an UNKNOWN_ERROR enum with a different message, and get rid of the code that attempts to retrieve the "error" from the response?

Copy link
Contributor

Choose a reason for hiding this comment

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

and get rid of the code that attempts to retrieve the "error" from the response

Not really, we should have that code but any failures in that code shuld just return as "UNKNOWN ERROR" (enum should be fine) instead of JSON_PARSING_ERROR as that json parsing error is not the real reason this request failed.

Comment on lines +63 to +66
if(responseCode == 403) {
onFailure(PersonalIdApiErrorCodes.FORBIDDEN_ERROR, null);
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

we should incorporate all critical reponse codes here -

401 - Auth failure
500 - Server Error
503/429 -> Rate Limited error / Too many requests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

9868847
I wasn't sure whether we also want unique error messages to be displayed to the user in these cases? For now I kept them all reporting "There was a network issue, please try again."

Copy link
Contributor

Choose a reason for hiding this comment

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

I do think we should have different messaging here on the lines of -
401 - You are not authorized to make this request
500 - There was an error processing your request.
503-429 -> Too many requests attemptes, try again after an hour.

@shubham1g5 shubham1g5 added the skip-integration-tests Skip android tests. label Jun 9, 2025
@OrangeAndGreen OrangeAndGreen requested a review from shubham1g5 June 9, 2025 18:42
@shubham1g5
Copy link
Contributor

@OrangeAndGreen I am merging this for now but think we must try and iterate on the couple outsatanding threads and get it in the release as it would be quite valuable to resolve any issues coming from users.

@shubham1g5 shubham1g5 merged commit 8ac30a8 into commcare_2.57 Jun 10, 2025
2 checks passed
@shubham1g5 shubham1g5 deleted the dv/personalid_api_error_handling branch June 10, 2025 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-integration-tests Skip android tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants