Skip to content

Commit f78b6fd

Browse files
authored
fix(ios): Start native SDK without options file (#6956)
RNSentrySDK fell back to parsing an empty dictionary when sentry.options.json was missing or could not be parsed. Cocoa reads a missing "dsn" key as an empty DSN and rejects it, so the fallback always failed and configureOptions never ran. Before 8.30.0 this crashed at launch; since then the native SDK silently never starts. Fall back to default SentryOptions instead, and skip the start only when neither the file nor configureOptions set a valid DSN, so that a later init from JS can still start the native SDK. Register RNSentryStartTests, RNSentryStartFromFileTests and their fixtures in the Cocoa tester project again. They were lost when #5582 landed and have not run since. Update them for Cocoa 9 and add regression tests for a DSN set in configureOptions.
1 parent f1f9f27 commit f78b6fd

6 files changed

Lines changed: 78 additions & 25 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
- Persist fatal JS error envelopes to disk on Android before the process exits ([#6937](https://github.com/getsentry/sentry-react-native/pull/6937))
1515
- The hard-crash envelope was only queued on the transport thread, so the event was lost when React Native terminated the process right after. It is now written to the offline cache on the calling thread and sent on the next launch, matching iOS.
1616
- Support Expo Router 58 in `expoRouterIntegration`. Metro no longer warns about the removed `expo-router/build/global-state/router-store` module, and the integration creates navigation spans again ([#6923](https://github.com/getsentry/sentry-react-native/pull/6923))
17+
- Start the native iOS SDK from `RNSentrySDK` when `sentry.options.json` is missing or can't be parsed and the DSN is set in `configureOptions` ([#6956](https://github.com/getsentry/sentry-react-native/pull/6956))
18+
- The fallback options always failed to parse, so `configureOptions` never ran. Before 8.30.0 the app crashed at launch; since then the native SDK silently never started.
1719

1820
### Internal
1921

‎packages/core/RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
3339C4832D6625570088EB3A /* RNSentryReplayControlTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3339C4822D6625570088EB3A /* RNSentryReplayControlTests.m */; };
1515
336084392C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 336084382C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift */; };
1616
3380C6C42CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3380C6C32CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift */; };
17+
339C6C3C2D3EB25100CA72ED /* RNSentryStartTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */; };
18+
339C6C422D3FD3AE00CA72ED /* RNSentryStartFromFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339C6C412D3FD39500CA72ED /* RNSentryStartFromFileTests.swift */; };
19+
339C6C482D3FD9A700CA72ED /* invalid.options.json in Resources */ = {isa = PBXBuildFile; fileRef = 339C6C462D3FD91900CA72ED /* invalid.options.json */; };
20+
339C6C492D3FD9A700CA72ED /* invalid.options.txt in Resources */ = {isa = PBXBuildFile; fileRef = 339C6C452D3FD90200CA72ED /* invalid.options.txt */; };
21+
339C6C4B2D3FD9B200CA72ED /* valid.options.json in Resources */ = {isa = PBXBuildFile; fileRef = 339C6C4A2D3FD9AB00CA72ED /* valid.options.json */; };
1722
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */; };
1823
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */; };
1924
33DEDFEA2D8DBE67006066E4 /* RNSentryOnDrawReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DEDFE92D8DBE5B006066E4 /* RNSentryOnDrawReporterTests.swift */; };
@@ -47,6 +52,11 @@
4752
3380C6C32CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryReplayPostInitTests.swift; sourceTree = "<group>"; };
4853
338739072A7D7D2800950DDD /* RNSentryReplay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSentryReplay.h; path = ../ios/RNSentryReplay.h; sourceTree = "<group>"; };
4954
33958C672BFCEF5A00AD1FB6 /* RNSentryOnDrawReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryOnDrawReporter.h; path = ../ios/RNSentryOnDrawReporter.h; sourceTree = "<group>"; };
55+
339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryStartTests.swift; sourceTree = "<group>"; };
56+
339C6C412D3FD39500CA72ED /* RNSentryStartFromFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryStartFromFileTests.swift; sourceTree = "<group>"; };
57+
339C6C452D3FD90200CA72ED /* invalid.options.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = invalid.options.txt; sourceTree = "<group>"; };
58+
339C6C462D3FD91900CA72ED /* invalid.options.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = invalid.options.json; sourceTree = "<group>"; };
59+
339C6C4A2D3FD9AB00CA72ED /* valid.options.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = valid.options.json; sourceTree = "<group>"; };
5060
33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryFramesTrackerListenerTests.m; sourceTree = "<group>"; };
5161
33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryDependencyContainerTests.m; sourceTree = "<group>"; };
5262
33AFE0132B8F31AF00AAB120 /* RNSentryDependencyContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryDependencyContainer.h; path = ../ios/RNSentryDependencyContainer.h; sourceTree = "<group>"; };
@@ -91,6 +101,7 @@
91101
3360896929524163007C7730 = {
92102
isa = PBXGroup;
93103
children = (
104+
339C6C432D3FD41C00CA72ED /* TestAssets */,
94105
33AFE0122B8F319000AAB120 /* RNSentry */,
95106
3360899029524164007C7730 /* RNSentryCocoaTesterTests */,
96107
3360897329524163007C7730 /* Products */,
@@ -131,6 +142,8 @@
131142
3380C6C32CE25ECA0018B9B6 /* RNSentryReplayPostInitTests.swift */,
132143
26C9B2501FF3EBD8B91BA6C1 /* RNSentryStartInvalidOptionsTests.swift */,
133144
864F5BFD9CFDA4502ADAE72E /* RNSentrySDKStartInvalidOptionsTests.swift */,
145+
339C6C3B2D3EB23B00CA72ED /* RNSentryStartTests.swift */,
146+
339C6C412D3FD39500CA72ED /* RNSentryStartFromFileTests.swift */,
134147
);
135148
path = RNSentryCocoaTesterTests;
136149
sourceTree = "<group>";
@@ -144,6 +157,16 @@
144157
path = Replay;
145158
sourceTree = "<group>";
146159
};
160+
339C6C432D3FD41C00CA72ED /* TestAssets */ = {
161+
isa = PBXGroup;
162+
children = (
163+
339C6C4A2D3FD9AB00CA72ED /* valid.options.json */,
164+
339C6C462D3FD91900CA72ED /* invalid.options.json */,
165+
339C6C452D3FD90200CA72ED /* invalid.options.txt */,
166+
);
167+
path = TestAssets;
168+
sourceTree = "<group>";
169+
};
147170
33AFE0122B8F319000AAB120 /* RNSentry */ = {
148171
isa = PBXGroup;
149172
children = (
@@ -181,6 +204,7 @@
181204
3360898929524164007C7730 /* Sources */,
182205
BB7D14838753E6599863899B /* Frameworks */,
183206
CC7959F3721CB3AD7CB6A047 /* [CP] Copy Pods Resources */,
207+
339C6C472D3FD99900CA72ED /* Resources */,
184208
);
185209
buildRules = (
186210
);
@@ -225,6 +249,19 @@
225249
};
226250
/* End PBXProject section */
227251

252+
/* Begin PBXResourcesBuildPhase section */
253+
339C6C472D3FD99900CA72ED /* Resources */ = {
254+
isa = PBXResourcesBuildPhase;
255+
buildActionMask = 2147483647;
256+
files = (
257+
339C6C482D3FD9A700CA72ED /* invalid.options.json in Resources */,
258+
339C6C4B2D3FD9B200CA72ED /* valid.options.json in Resources */,
259+
339C6C492D3FD9A700CA72ED /* invalid.options.txt in Resources */,
260+
);
261+
runOnlyForDeploymentPostprocessing = 0;
262+
};
263+
/* End PBXResourcesBuildPhase section */
264+
228265
/* Begin PBXShellScriptBuildPhase section */
229266
30F19D4E16BEEFEC68733838 /* [CP] Check Pods Manifest.lock */ = {
230267
isa = PBXShellScriptBuildPhase;
@@ -289,6 +326,8 @@
289326
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */,
290327
00C7702ED388EA0682DBCC48 /* RNSentryStartInvalidOptionsTests.swift in Sources */,
291328
AC4EE11A6E697E75C3753C16 /* RNSentrySDKStartInvalidOptionsTests.swift in Sources */,
329+
339C6C3C2D3EB25100CA72ED /* RNSentryStartTests.swift in Sources */,
330+
339C6C422D3FD3AE00CA72ED /* RNSentryStartFromFileTests.swift in Sources */,
292331
);
293332
runOnlyForDeploymentPostprocessing = 0;
294333
};

‎packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentrySDKStartInvalidOptionsTests.swift‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import XCTest
33

44
final class RNSentrySDKStartInvalidOptionsTests: XCTestCase {
55

6-
// When neither the options file nor the fallback can produce options,
7-
// the native auto-init path must not start the SDK with nil options.
6+
// When neither the options file nor configureOptions provides a DSN,
7+
// the native auto-init path must not start the SDK.
88
func testStartWithUnparseableOptionsFileDoesNotStartSdk() {
99
// Arrange
1010
SentrySDK.close()
1111

12-
// Act — a non-existent options path forces the nil-options fallback
12+
// Act — a non-existent options path and no configureOptions leave no DSN
1313
RNSentrySDK.start("/does/not/exist/sentry.options.json", configureOptions: nil)
1414

1515
// Assert

‎packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryStartFromFileTests.swift‎

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import XCTest
33

44
final class RNSentryStartFromFileTests: XCTestCase {
55

6+
override func setUp() {
7+
super.setUp()
8+
SentrySDK.close()
9+
}
10+
611
func testNoThrowOnMissingOptionsFile() {
712
var wasConfigurationCalled = false
813

@@ -11,10 +16,16 @@ final class RNSentryStartFromFileTests: XCTestCase {
1116
})
1217

1318
XCTAssertTrue(wasConfigurationCalled)
19+
XCTAssertFalse(SentrySDK.isEnabled)
20+
}
1421

15-
let actualOptions = SentrySDK.internal.options
16-
XCTAssertNil(actualOptions.dsn)
17-
XCTAssertNil(actualOptions.parsedDsn)
22+
func testStartsWithConfigureDsnOnMissingOptionsFile() {
23+
RNSentrySDK.start(getNonExistingOptionsPath(), configureOptions: { options in
24+
options.dsn = "https://abcd@efgh.ingest.sentry.io/123456"
25+
})
26+
27+
XCTAssertTrue(SentrySDK.isEnabled)
28+
XCTAssertEqual(SentrySDK.internal.options.dsn, "https://abcd@efgh.ingest.sentry.io/123456")
1829
}
1930

2031
func testNoThrowOnInvalidFileType() {
@@ -25,10 +36,16 @@ final class RNSentryStartFromFileTests: XCTestCase {
2536
})
2637

2738
XCTAssertTrue(wasConfigurationCalled)
39+
XCTAssertFalse(SentrySDK.isEnabled)
40+
}
2841

29-
let actualOptions = SentrySDK.internal.options
30-
XCTAssertNil(actualOptions.dsn)
31-
XCTAssertNil(actualOptions.parsedDsn)
42+
func testStartsWithConfigureDsnOnInvalidFileType() {
43+
RNSentrySDK.start(getInvalidOptionsTypePath(), configureOptions: { options in
44+
options.dsn = "https://abcd@efgh.ingest.sentry.io/123456"
45+
})
46+
47+
XCTAssertTrue(SentrySDK.isEnabled)
48+
XCTAssertEqual(SentrySDK.internal.options.dsn, "https://abcd@efgh.ingest.sentry.io/123456")
3249
}
3350

3451
func testNoThrowOnInvalidOptions() {
@@ -41,7 +58,7 @@ final class RNSentryStartFromFileTests: XCTestCase {
4158
XCTAssertTrue(wasConfigurationCalled)
4259

4360
let actualOptions = SentrySDK.internal.options
44-
XCTAssertNil(actualOptions.dsn)
61+
XCTAssertEqual(actualOptions.dsn, "https://abcd@efgh.ingest.sentry.io/123456")
4562
XCTAssertNotNil(actualOptions.parsedDsn)
4663
XCTAssertEqual(actualOptions.environment, "environment-from-invalid-file")
4764
}
@@ -56,7 +73,7 @@ final class RNSentryStartFromFileTests: XCTestCase {
5673
XCTAssertTrue(wasConfigurationCalled)
5774

5875
let actualOptions = SentrySDK.internal.options
59-
XCTAssertNil(actualOptions.dsn)
76+
XCTAssertEqual(actualOptions.dsn, "https://abcd@efgh.ingest.sentry.io/123456")
6077
XCTAssertNotNil(actualOptions.parsedDsn)
6178
XCTAssertEqual(actualOptions.environment, "environment-from-valid-file")
6279
}

‎packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryStartTests.swift‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ final class RNSentryStartTests: XCTestCase {
1111
XCTAssertFalse(actualOptions!.enableCaptureFailedRequests)
1212
XCTAssertNil(actualOptions!.tracesSampleRate)
1313
XCTAssertNil(actualOptions!.tracesSampler)
14-
XCTAssertFalse(actualOptions!.enableTracing)
1514
}
1615

1716
func testStartSetsReactDeafults() {

‎packages/core/ios/RNSentrySDK.m‎

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,22 @@ + (void)start:(NSString *)path configureOptions:(void (^)(SentryOptions *options
5959

6060
if (options == nil) {
6161
// Fallback in case that options file could not be parsed.
62-
NSError *fallbackError = nil;
63-
options = [RNSentryInternal optionsFromDictionary:@{ } error:&fallbackError];
64-
if (fallbackError != nil) {
65-
NSLog(@"[RNSentry] Failed to create fallback options with error: %@",
66-
fallbackError.localizedDescription);
67-
}
68-
}
69-
70-
if (options == nil) {
71-
// Even the fallback failed. Skip starting instead of passing nil options to
72-
// the native SDK, which would crash it.
73-
NSLog(@"[RNSentry] Could not create options; skipping native SDK start.");
74-
return;
62+
options = [[SentryOptions alloc] init];
7563
}
7664

7765
[RNSentryStart updateWithReactDefaults:options];
7866
if (configureOptions != nil) {
7967
configureOptions(options);
8068
}
8169
[RNSentryStart updateWithReactFinals:options];
70+
71+
if (options.parsedDsn == nil) {
72+
// Don't start without a DSN, so that a later init from JS can still start the native SDK.
73+
NSLog(@"[RNSentry] No valid DSN in the options file or configureOptions; skipping native "
74+
@"SDK start.");
75+
return;
76+
}
77+
8278
[RNSentryStart startWithOptions:options];
8379
}
8480

0 commit comments

Comments
 (0)