objectbox / objectbox/objectbox-swift
Crash when schema is deactivated
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 619
- Forks
- 37
- PR merge metrics
- No merged PRs in 30d
Description
Is there an existing issue?
- I have searched existing issues
Build info
- ObjectBox version: 5.3.0 (Swift Package Manager,
objectbox-swift-spm5.3.0) - OS: iPadOS 26.2 – 26.6.2
- Device/chipset: several iPads, all affected: iPad12,1 (iPad 9th gen, A13), iPad11,6 (iPad 8th gen, A12), iPad13,2 (iPad Air 4th gen, A14), iPad15,5 (iPad Air 13"
M3), iPad15,7 (iPad 11th gen, A16)
Xcode 26.0.1, Swift 6.2. Self-hosted ObjectBox Sync Server with JWT authentication.
Steps to reproduce
- Create a sync client, set a
loginListener, and start it against a Sync Server. Let it log in. - On the server, mark the client's current data model as inactive, so the server rejects it.
- The client logs in again (in our case, every connected client did so immediately).
- The app crashes in
loginFailureCallback.
We have also noted, that if you upload schemas to the Sync Server UI, once you have more than 6 live schema versions and make a new schema active, the ui marks all previous schemas as inactive, exacerbating the issue.
Expected behavior
The app shouldn't crash when the server reports a login failure with a code that SyncCode doesn't list. The unknown code should map to a catch-all (e.g. .unknown) and be passed to SyncLoginListener.loginFailed(result:).
A dedicated SyncCode case for model rejection would also help, so apps can tell it apart from credential failures.
Actual behavior
Every connected iOS client crashed within the same second, with EXC_BREAKPOINT / SIGTRAP on the sync websocket thread (ClLws).
The cause is a force-unwrap in SyncClientImpl.swift (line 432 in v5.3.0):
private func loginFailureCallback(_ userData: UnsafeMutableRawPointer?, _ cCode: OBXSyncCode) {
let code = SyncCode(rawValue: cCode.rawValue)! // traps for codes not in the enum
callWithSyncClient(userData, action: { (client: SyncClient) in
client.loginListener?.loginFailed(result: code)
})
}
The crash happens before our SyncLoginListener runs, so the app can't catch it. The only workaround we found is to never set loginListener, because obx_sync_listener_login_failure is only registered when it's non-nil. But that breaks real login-failure handling, such as refreshing an expired JWT on .credentialsRejected.
Suggested fix:
let code = SyncCode(rawValue: cCode.rawValue) ?? .unknown
Code
Code
import ObjectBox
final class LoginHandler: SyncLoginListener {
func loggedIn() {
print("logged in")
}
func loginFailed(result: SyncCode) {
// Never reached for unknown codes; the app traps in loginFailureCallback first
print("login failed: \(result)")
}
}
let loginHandler = LoginHandler()
let client = try Sync.makeClient(
store: store,
urlString: "wss://your-sync-server:443",
credentials: SyncCredentials.makeJwtAccessToken(token)
)
client.loginListener = loginHandler // registers obx_sync_listener_login_failure
try client.start()
// Now deactivate the client's data model on the Sync Server
Logs, stack traces
Logs
Crashed thread (symbolicated):
Thread: ClLws (crashed) EXC_BREAKPOINT / SIGTRAP
0 ObjectBox loginFailureCallback SyncClientImpl.swift:432
1 ObjectBox <internal>
2 ObjectBox <internal>
3 ObjectBox <internal>
4 ObjectBox <internal>
5 ObjectBox <internal>
6 ObjectBox lws_get_close_payload
7 ObjectBox lws_get_close_payload
8 ObjectBox lws_hdr_custom_copy
9 ObjectBox lws_get_close_payload
10 ObjectBox lws_service_fd_tsi
11 ObjectBox _lws_plat_service_tsi
12 ObjectBox lws_service
13 ObjectBox <internal>
14 ObjectBox <internal>
15 libsystem_pthread.dylib _pthread_start
Our Flutter app, with the same scenario, logged this natively and did not crash:
I/Box: [ClComm] The data model was rejected by the server
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in SyncClientImpl.swift at loginFailureCallback, especially the force-unwrap on line 432, and inspect how SyncCode handles values from the sync callback. Reproduce the scenario by deactivating the client's data model on the Sync Server, then verify that an unknown code reaches SyncLoginListener.loginFailed(result:) without crashing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ios, swift
- Domain
- database, mobile
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100