Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
- On a busy device during cold start this could stall the launcher activity's `onCreate` for hundreds of milliseconds.
- Do not inspect the response body of streaming Apollo 3 and 4 responses (`multipart/mixed`, `text/event-stream`) for failed requests, which blocked `@defer` and `@stream` ([#6432](https://github.com/getsentry/sentry-java/pull/6432))
- Set Apollo 3, 4, and 5 HTTP spans to `CANCELLED` instead of `INTERNAL_ERROR` when the request is cancelled ([#6284](https://github.com/getsentry/sentry-java/pull/6284))
- Stop swallowing fatal errors such as `OutOfMemoryError` in the SDK code that loads native libraries and optional dependencies ([#6562](https://github.com/getsentry/sentry-java/pull/6562))
- The offline cache no longer deletes its queue file when it runs out of memory while reading it.
- The Ktor client integration no longer swallows coroutine cancellation while reading the response body of a failed request.

### Improvements

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ private void ensureLifecycleObserver(final @NotNull ILogger logger) {
SentryLevel.WARNING,
"androidx.lifecycle is not available, some features might not be properly working,"
+ "e.g. Session Tracking, Network and System Events breadcrumbs, etc.");
} catch (Throwable e) {
} catch (LinkageError e) {
// androidx.lifecycle has an incompatible version
logger.log(SentryLevel.ERROR, "AppState could not register lifecycle observer", e);
}
}
Expand All @@ -124,7 +125,7 @@ private void addObserverInternal(final @NotNull ILogger logger) {
if (observerRef != null) {
ProcessLifecycleOwner.get().getLifecycle().addObserver(observerRef);
}
} catch (Throwable e) {
} catch (LinkageError e) {
// This is to handle a potential 'AbstractMethodError' gracefully. The error is triggered in
// connection with conflicting dependencies of the androidx.lifecycle.
// //See the issue here: https://github.com/getsentry/sentry-java/pull/2228
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static String id(final @NotNull Context context) throws RuntimeException
return deviceId;
}
deviceId = readInstallationFile(installation);
} catch (Throwable e) {
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ public final void register(final @NotNull IScopes scopes, final @NotNull SentryO
this.options
.getLogger()
.log(SentryLevel.ERROR, "Failed to invoke the SentryNdk.init method.", e);
} catch (Throwable e) {
} catch (ReflectiveOperationException e) {
// InvocationTargetException wraps whatever SentryNdk.init threw, such as an
// UnsatisfiedLinkError when the native library failed to load
disableNdkIntegration(this.options);
this.options.getLogger().log(SentryLevel.ERROR, "Failed to initialize SentryNdk.", e);
}
Expand Down Expand Up @@ -94,7 +96,7 @@ public void close() throws IOException {
options
.getLogger()
.log(SentryLevel.ERROR, "Failed to invoke the SentryNdk.close method.", e);
} catch (Throwable e) {
} catch (ReflectiveOperationException e) {
options.getLogger().log(SentryLevel.ERROR, "Failed to close SentryNdk.", e);
} finally {
disableNdkIntegration(this.options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,29 @@ public static ViewHierarchy snapshotViewHierarchy(
try {
viewHierarchy.set(snapshotViewHierarchy(decorView, exporters));
latch.countDown();
} catch (LinkageError e) {
// The Compose exporter calls Compose internals, which may not match the app's
// Compose version
logger.log(SentryLevel.ERROR, "Failed to process view hierarchy.", e);
} catch (Throwable t) {
// The snapshot calls into the app's own views, so we can't know what it throws.
// The event is sent without a view hierarchy.
ExceptionUtils.rethrowIfFatal(t);
logger.log(SentryLevel.ERROR, "Failed to process view hierarchy.", t);
}
});
if (latch.await(CAPTURE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
return viewHierarchy.get();
}
}
} catch (LinkageError e) {
// The Compose exporter calls Compose internals, which may not match the app's Compose
// version
logger.log(SentryLevel.ERROR, "Failed to process view hierarchy.", e);
} catch (Throwable t) {
// The snapshot calls into the app's own views, so we can't know what it throws. The event
// is sent without a view hierarchy.
ExceptionUtils.rethrowIfFatal(t);
logger.log(SentryLevel.ERROR, "Failed to process view hierarchy.", t);
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.SpanStatus;
import io.sentry.util.ExceptionUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -56,7 +57,16 @@ public boolean dispatchTouchEvent(final @Nullable MotionEvent motionEvent) {
final MotionEvent copy = motionEventObtainer.obtain(motionEvent);
try {
handleTouchEvent(copy);
} catch (LinkageError e) {
// The Compose gesture locator calls Compose internals, which may not match the app's
// Compose version
if (options != null) {
options.getLogger().log(SentryLevel.ERROR, "Error dispatching touch event", e);
}
} catch (Throwable e) {
// Locating the touched view calls into the app's own views, so we can't know what it
// throws. The gesture is not recorded, and the touch still reaches the app.
ExceptionUtils.rethrowIfFatal(e);
if (options != null) {
options.getLogger().log(SentryLevel.ERROR, "Error dispatching touch event", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.sentry.ndk.NativeModuleListLoader;
import io.sentry.protocol.DebugImage;
import io.sentry.util.AutoClosableReentrantLock;
import io.sentry.util.ExceptionUtils;
import io.sentry.util.Objects;
import java.util.ArrayList;
import java.util.HashSet;
Expand Down Expand Up @@ -71,7 +72,13 @@ public DebugImagesLoader(
.getLogger()
.log(SentryLevel.DEBUG, "Debug images loaded: %d", debugImages.size());
}
} catch (LinkageError e) {
// e.g. UnsatisfiedLinkError when the native library failed to load
options.getLogger().log(SentryLevel.ERROR, e, "Failed to load debug images.");
} catch (Throwable e) {
// A JNI call into sentry-native, so we can't know what it throws. The event is sent
// without debug images.
ExceptionUtils.rethrowIfFatal(e);
options.getLogger().log(SentryLevel.ERROR, e, "Failed to load debug images.");
}
}
Expand Down Expand Up @@ -172,7 +179,13 @@ public void clearDebugImages() {
moduleListLoader.clearModuleList();

options.getLogger().log(SentryLevel.INFO, "Debug images cleared.");
} catch (LinkageError e) {
// e.g. UnsatisfiedLinkError when the native library failed to load
options.getLogger().log(SentryLevel.ERROR, e, "Failed to clear debug images.");
} catch (Throwable e) {
// A JNI call into sentry-native, so we can't know what it throws. The cached list is
// cleared either way.
ExceptionUtils.rethrowIfFatal(e);
options.getLogger().log(SentryLevel.ERROR, e, "Failed to clear debug images.");
}
debugImages = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ private SentryNdk() {}
try {
//noinspection UnstableApiUsage
io.sentry.ndk.SentryNdk.loadNativeLibraries();
} catch (Throwable t) {
// ignored
// if loadLibrary() fails, the later init() will throw an exception anyway
} catch (LinkageError ignored) {
// e.g. UnsatisfiedLinkError. The later init() throws too, which disables the NDK
// integration.
} finally {
loadLibraryLatch.countDown();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ internal object WindowSpy {
lazy(NONE) {
try {
Class.forName("com.android.internal.policy.DecorView")
} catch (ignored: Throwable) {
} catch (ignored: ClassNotFoundException) {
Log.d("WindowSpy", "Unexpected exception loading DecorView on API $SDK_INT", ignored)
null
}
Expand Down Expand Up @@ -171,7 +171,7 @@ internal object WindowManagerSpy {
val className = "android.view.WindowManagerGlobal"
try {
Class.forName(className)
} catch (ignored: Throwable) {
} catch (ignored: ClassNotFoundException) {
Log.w("WindowManagerSpy", ignored)
null
}
Expand All @@ -198,7 +198,10 @@ internal object WindowManagerSpy {
mViewsField[windowManagerInstance] = swap(mViews)
}
}
} catch (ignored: Throwable) {
} catch (ignored: ReflectiveOperationException) {
Log.w("WindowManagerSpy", ignored)
} catch (ignored: ClassCastException) {
// mViews is not an ArrayList on this device
Log.w("WindowManagerSpy", ignored)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.findRootCoordinates
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.text.TextLayoutResult
import io.sentry.util.ExceptionUtils
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.roundToInt
Expand Down Expand Up @@ -93,7 +94,7 @@ internal fun LayoutNode.findPainter(): Painter? {
return try {
modifier::class.java.getDeclaredField("painter").apply { isAccessible = true }.get(modifier)
as? Painter
} catch (e: Throwable) {
} catch (_: ReflectiveOperationException) {
null
}
}
Expand Down Expand Up @@ -136,7 +137,12 @@ internal fun LayoutNode.findTextColor(): Color? {
(modifier::class.java.getDeclaredField("color").apply { isAccessible = true }.get(modifier)
as? ColorProducer)
?.invoke()
} catch (_: LinkageError) {
// older Compose versions don't have ColorProducer
null
} catch (e: Throwable) {
// ColorProducer.invoke() runs the app's color lambda, so we can't know what it throws
ExceptionUtils.rethrowIfFatal(e)
null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import io.sentry.android.replay.util.toRect
import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.GenericViewHierarchyNode
import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.ImageViewHierarchyNode
import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.TextViewHierarchyNode
import io.sentry.util.ExceptionUtils
import java.lang.ref.WeakReference
import java.lang.reflect.Method

Expand All @@ -45,7 +46,7 @@ internal object ComposeViewHierarchyNode {
.java
.getDeclaredMethod("getCollapsedSemantics\$ui_release")
.apply { isAccessible = true }
} catch (_: Throwable) {
} catch (_: NoSuchMethodException) {
// ignore, as this method may not be available
}
return@lazy null
Expand All @@ -57,7 +58,7 @@ internal object ComposeViewHierarchyNode {
internal fun retrieveSemanticsConfiguration(node: LayoutNode): SemanticsConfiguration? {
return try {
node.semanticsConfiguration
} catch (t: Throwable) {
} catch (t: NoSuchMethodError) {
// for backwards compatibility
// Jetpack Compose 1.8 or older
if (getCollapsedSemanticsMethod != null) {
Expand Down Expand Up @@ -128,16 +129,27 @@ internal object ComposeViewHierarchyNode {
}

val visibleRect = node.coordinates.boundsInWindow(_rootCoordinates?.get())
val semantics: SemanticsConfiguration?
var semanticsFailure: Throwable? = null
val semantics: SemanticsConfiguration? =
try {
retrieveSemanticsConfiguration(node)
} catch (e: LinkageError) {
// an unsupported Compose version, which must not crash the app
semanticsFailure = e
null
} catch (t: Throwable) {
// semantics modifiers run app code, so we can't know what this throws
ExceptionUtils.rethrowIfFatal(t)
semanticsFailure = t
null
}

try {
semantics = retrieveSemanticsConfiguration(node)
} catch (t: Throwable) {
if (semanticsFailure != null) {
if (!semanticsRetrievalErrorLogged) {
semanticsRetrievalErrorLogged = true
logger.log(
SentryLevel.ERROR,
t,
semanticsFailure,
"""
Error retrieving semantics information from Compose tree. Most likely you're using
an unsupported version of androidx.compose.ui:ui. The supported
Expand All @@ -152,7 +164,7 @@ internal object ComposeViewHierarchyNode {
// fail fast in our own sample/UI-test apps (see SentryReplayDebug), so regressions surface
// as crashes instead of silently degrading masking
if (SentryReplayDebug.failFast) {
throw t
throw semanticsFailure
}

// If we're unable to retrieve the semantics configuration
Expand Down Expand Up @@ -284,13 +296,23 @@ internal object ComposeViewHierarchyNode {
return false
}

var traversalFailure: Throwable? = null
try {
val rootNode = (view as? Owner)?.root ?: return false
rootNode.traverse(parent, isComposeRoot = true, options, logger)
} catch (e: LinkageError) {
// an unsupported Compose version, which must not crash the app
traversalFailure = e
} catch (e: Throwable) {
// the traversal runs app code, so we can't know what it throws
ExceptionUtils.rethrowIfFatal(e)
traversalFailure = e
}

if (traversalFailure != null) {
logger.log(
SentryLevel.ERROR,
e,
traversalFailure,
"""
Error traversing Compose tree. Most likely you're using an unsupported version of
androidx.compose.ui:ui. The minimum supported version is 1.5.0. If it's a newer
Expand All @@ -302,7 +324,7 @@ internal object ComposeViewHierarchyNode {
// fail fast in our own sample/UI-test apps (see SentryReplayDebug), so regressions surface
// as crashes instead of silently skipping the whole Compose subtree (i.e. not masking it)
if (SentryReplayDebug.failFast) {
throw e
throw traversalFailure
}
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ internal class SentryComposeHelper(logger: ILogger) {
val value = sentryTagElementField.get(modifier)
return value as String?
}
} catch (e: Throwable) {
// ignored
} catch (_: IllegalAccessException) {
// fall back to SemanticsModifier below
} catch (_: ClassCastException) {
// the tag is not a String in this Compose version
}

// Older versions use SemanticsModifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import io.sentry.exception.ExceptionMechanismException
import io.sentry.exception.SentryHttpClientException
import io.sentry.protocol.Mechanism
import io.sentry.util.CookieUtils
import io.sentry.util.ExceptionUtils
import io.sentry.util.HttpUtils
import io.sentry.util.UrlUtils
import kotlin.coroutines.cancellation.CancellationException

internal object SentryKtorClientUtils {
internal suspend fun captureClientError(
Expand Down Expand Up @@ -51,7 +53,12 @@ internal object SentryKtorClientUtils {
statusCode = response.status.value
try {
bodySize = response.bodyAsBytes().size.toLong()
} catch (_: Throwable) {}
} catch (e: CancellationException) {
throw e
} catch (@Suppress("TooGenericExceptionCaught") e: Throwable) {
// The HTTP engine can throw anything while reading the body. The body size is optional.
ExceptionUtils.rethrowIfFatal(e)
}
}

event.request = sentryRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.ContextStorageProvider;
import io.sentry.util.ExceptionUtils;
import java.util.Iterator;
import java.util.ServiceLoader;
import org.jetbrains.annotations.NotNull;
Expand All @@ -23,8 +24,11 @@ public ContextStorage get() {
return contextStorageProvider.get();
}
}
} catch (LinkageError ignored) {
// a provider needs a class that is missing or has an incompatible version, use fallback
} catch (Throwable t) {
// ignore and use fallback
// Other providers are third-party code, so we can't know what they throw. Use fallback.
ExceptionUtils.rethrowIfFatal(t);
}

// using default / fallback storage
Expand Down
Loading
Loading