objectbox / objectbox/objectbox-swift

Crash when schema is deactivated

Open Beginner friendly
#115 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Swift
Stars
619
Forks
37
PR merge metrics
No merged PRs in 30d

Description

Is there an existing issue?
Build info
  • ObjectBox version: 5.3.0 (Swift Package Manager, objectbox-swift-spm 5.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
  1. Create a sync client, set a loginListener, and start it against a Sync Server. Let it log in.
  2. On the server, mark the client's current data model as inactive, so the server rejects it.
  3. The client logs in again (in our case, every connected client did so immediately).
  4. 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.