Skip to content

ref: Narrow Throwable catches to the exceptions they guard (JAVA-552) - #6564

Draft
runningcode wants to merge 3 commits into
9.x.xfrom
no/java-552-narrow-throwable-catches
Draft

runningcode wants to merge 3 commits into
9.x.xfrom
no/java-552-narrow-throwable-catches

Conversation

@runningcode

@runningcode runningcode commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

📜 Description

Replace 80 catch (Throwable) with the exceptions the guarded code can throw. SDK bugs and fatal
errors at these sites are no longer swallowed:

  • Executors (37): RejectedExecutionException around submit and schedule, which
    ISentryExecutorService declares.
    • SendCachedEnvelopeIntegration already caught it. Its broad arm is replaced by handling
      InterruptedException (restoring the interrupt flag) and ExecutionException from
      future.get(), next to the existing TimeoutException.
  • File I/O (23): IOException. SecurityException for File.delete, renameTo and
    exists, which can throw nothing else.
  • Parsing and the rest (20): the specific types: NumberFormatException,
    IllegalArgumentException, MalformedURLException, URISyntaxException,
    PatternSyntaxException, and so on.
    • Baggage's key/value parsing also catches IndexOutOfBoundsException, which an incoming entry
      without = triggers.

The 15 catches that guard nothing are removed in #6565 instead.

Test changes

Several tests only passed because these catches hid mocks that don't behave like the real
objects. They now do:

  • The Replay executor mocks returned Unit from submit instead of a Future.
  • ContextUtilsTestHelper.createMockContext gave the mocked Context and Application no
    assets. A real Context always has them.
  • RootCheckerTest's mocked Runtime.exec returned null.
  • EnvelopeCacheTest faked a session write failure with a RuntimeException; it now throws an
    IOException.
  • The batch processor tests make submit throw RejectedExecutionException instead of a plain
    RuntimeException.

💡 Motivation and Context

This is part of the catch (Throwable) audit:

The three PRs don't depend on each other. Baggage.java conflicts with #6565, which re-indents
the per-entry catch narrowed here, so whichever PR merges second needs a rebase.

Six sites the audit had marked for narrowing are left for the boundary PR:

  • DeviceInfoUtil.getExternalStorageStat calls context.getExternalFilesDirs, a framework call.
  • The Apollo 3 and 4 request-body writeTo runs the app's own upload code.
  • The view id lookup in ViewHierarchyEventProcessor is part of the walk over the app's views.
  • BuildInfoProvider.isEmulator and ContextUtils.getFamily read Build.* fields. Making them
    null-safe changes their signatures.

💚 How did you test it?

Ran the unit tests of sentry, sentry-android-core, sentry-android-ndk,
sentry-android-replay, sentry-async-profiler, sentry-apollo-3, sentry-apollo-4,
sentry-jdbc, sentry-opentelemetry-core and sentry-opentelemetry-otlp, plus detekt for the
Kotlin modules.

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.

🔮 Next steps

  • Add rethrowIfFatal and a comment saying why the catch is broad to the boundary sites, one PR
    per module group.

🤖 Generated with Claude Code

Replace catch (Throwable) with the types the guarded code can throw, so SDK bugs and fatal
errors are no longer swallowed: RejectedExecutionException around executor submit and
schedule, IOException and SecurityException around file I/O, and the specific parse
exceptions elsewhere. Drop the try where nothing in it can throw.

Several tests only passed because these catches hid broken mocks: executor mocks that
returned Unit from submit, mock Contexts without assets, a Runtime.exec that returned null.
Make those mocks behave like the real objects.

Refs JAVA-552

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Nov 11, 2026

Copy link
Copy Markdown

JAVA-552

@github-actions

Copy link
Copy Markdown
Contributor

🚨 Detected changes in high risk code 🚨

High-risk code has higher potential to break the SDK and may be hard to test. To prevent severe bugs, apply the rollout process for releasing such changes and be extra careful when changing and reviewing these files:

  • sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java

@sentry

sentry Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.63.0 (1) release

⚙️ sentry-android Build Distribution Settings

Their removal is in no/java-552-remove-unneeded-throwable-catches, so this branch only narrows.

Refs JAVA-552

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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