Skip to content

fix(ios): return after the "No session available" callback in getTag - #836

Open
Bram-dc wants to merge 1 commit into
revtel:mainfrom
Bram-dc:fix/gettag-double-callback
Open

Bram-dc wants to merge 1 commit into
revtel:mainfrom
Bram-dc:fix/gettag-double-callback

Conversation

@Bram-dc

@Bram-dc Bram-dc commented Aug 20, 2026

Copy link
Copy Markdown

Fixes #833.

The bug

getTag() handles the no-session case by invoking the callback with an error, but does not return. Execution continues past the if (tagSession != nil) block to the shared success callback at the end:

if (tagSession != nil) {
    ...
} else {
    callback(@[@"No session available", [NSNull null]]);
    // no return
}

if (ndefTag) { ... return; }

callback(@[[NSNull null], rnTag]);   // <- runs for the no-session case too

ndefTag is still nil on that path, so the second callback(...) always runs. One getTag() call invokes its callback twice.

Why it matters now

On the old architecture the extra invocation was silently dropped. Under the New Architecture's TurboModule interop the callback is wrapped in a one-shot guard, and the second call trips a glog CHECK:

callback arg cannot be called more than once

which aborts the process (SIGABRT), rather than throwing something catchable in JS.

It is easy to reach: getTag() right after a scan is cancelled, or after the session has already closed, takes the no-session branch every time.

The fix

One line. return after the error callback, matching how the other early-exit paths in this method already behave.

I have been running this patched into 3.17.2 in production for several weeks and it removes the crash. The same missing return is present on the v4 branch in ios/NfcManager.mm, if you would like me to open a matching PR there.

getTag() reports the missing session through the callback but then falls
through to the shared `callback(@[[NSNull null], rnTag])` at the end of the
branch, so the callback runs twice for a single call.

On the old architecture the second invocation was silently ignored. Under
the New Architecture's TurboModule interop it is fatal: the callback is
wrapped in a one-shot guard and the second call trips a glog CHECK
("callback arg cannot be called more than once"), aborting the process.

This is easy to hit in practice, because getTag() is a natural thing to
call right after a scan is cancelled or the session has already closed.

Fixes revtel#833
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.

iOS: getTag() invokes its callback twice when no session is active, fatal under the New Architecture

1 participant