Parse-SDK-iOS-OSX
Parse-SDK-iOS-OSX copied to clipboard
objectId should be non nil
Hi,
Since a few weeks, at app launch, for some users (±0.1%), the app crashes on PFRESTObjectCommand.m line 21 with this error:
Fatal Exception: NSInvalidArgumentException
objectId should be non nil
At launch, we sync the current PFUser and we call PFUser.enableAutomaticUser()
.
I don't know which object could have a nil objectId.
If I add some debug, currentUser.objectId and currentUser.session are OK.
PFObject._validateFetchAsync
is called just before Just before PFRestObjectCommand.fetchObjectCommandForObjectState:withSessionToken
and it doesn't catch some invalid objectId.
A re-installation of the app correct the problem.
Do you have any idea where this might be coming from?
trace
objectId should be non nil
Fatal Exception: NSInvalidArgumentException
0 CoreFoundation 0x1a3488300 __exceptionPreprocess
1 libobjc.A.dylib 0x1a319cc1c objc_exception_throw
2 CoreFoundation 0x1a3377e68 -[NSCache init]
3 Parse 0x101423cc8 +[PFRESTObjectCommand fetchObjectCommandForObjectState:withSessionToken:] + 21 (PFRESTObjectCommand.m:21)
4 Parse 0x1013eff8c __56-[PFObjectController fetchObjectAsync:withSessionToken:]_block_invoke.10 + 55 (PFObjectController.m:55)
5 Bolts 0x100ea1f94 __62-[BFTask continueWithExecutor:successBlock:cancellationToken:]_block_invoke + 398 (BFTask.m:398)
6 Bolts 0x100ea198c __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 331 (BFTask.m:331)
7 Bolts 0x100e9f5ac __29+[BFExecutor defaultExecutor]_block_invoke_2 + 66 (BFExecutor.m:66)
8 Bolts 0x100e9fad8 -[BFExecutor execute:] + 131 (BFExecutor.m:131)
9 Bolts 0x100ea158c -[BFTask runContinuations] + 308 (BFTask.m:308)
10 Bolts 0x100ea10f4 -[BFTask trySetResult:] + 247 (BFTask.m:247)
11 Bolts 0x100ea25bc -[BFTaskCompletionSource setResult:] + 45 (BFTaskCompletionSource.m:45)
12 Bolts 0x100ea1b90 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 + 340 (BFTask.m:340)
13 Bolts 0x100ea1a40 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 348 (BFTask.m:348)
14 libdispatch.dylib 0x1a3126ec4 _dispatch_call_block_and_release
15 libdispatch.dylib 0x1a312833c _dispatch_client_callout
16 libdispatch.dylib 0x1a312a7b4 _dispatch_queue_override_invoke
17 libdispatch.dylib 0x1a31375c0 _dispatch_root_queue_drain
18 libdispatch.dylib 0x1a3137d9c _dispatch_worker_thread2
19 libsystem_pthread.dylib 0x1a318f6d8 _pthread_wqthread
20 libsystem_pthread.dylib 0x1a31959c8 start_wqthread
@TomWFox @mtrezza have you already seen something like this?
Why is there even a [BFTask continueWithExecutor
before the _validateFetchAsync
test whereas it's a sync test (just a if
)? Can't we removed that to avoid any potential race condition issue?
BTW, the validation test the existence of the objectId
whereas the assert test the length of that objectId
, so that a weird ""
objectId could pass the first test, but fail on the assert (I don't know how we could have a ""
objectId of course)
If I add some debug, currentUser.objectId and currentUser.session are OK.
Where are they 'okay'? Are they are non-nil when the check claims they're nil?
In application:didFinishLaunchingWithOptions
, after Parse init, objectIds are never nil and sessions are valid (except for new users but it is normal and it doesn't crash).
In all debug prints in app, they are never nil and always valid before the crash, from what I can see.
@carbonimax That sounds pretty strange. The only times I recall seeing similar behaviour (and not in Parse) involved threading issues. Where values were being set on one thread and tested in another.
I'm not sure that's what you're seeing here (I recall PFObjects being thread-safe in fact), but it's the first thing that jumps to mind.
Since a few weeks, at app launch, for some users (±0.1%), the app crashes on PFRESTObjectCommand.m line 21
Does the date coincide with an app update / release date (mind the rollout duration)? Then again, "some users (±0.1%)" doesn't sound like a significant volume :)
have you already seen something like this?
I have seen a similar issue regarding PFUser.currentUser()
on iOS, I assumed a threading issue. That is what I would be looking at here at first. For example when the current user is logged in or out, while there are requests being made on different threads.
Does someone have any idea of the purpose of the first BFTask in:
- (BFTask *)fetchObjectAsync:(PFObject *)object withSessionToken:(NSString *)sessionToken {
@weakify(self);
return [[[[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
return [object _validateFetchAsync];
}] continueWithSuccessBlock:^id(BFTask *task) {
@strongify(self);
PFRESTCommand *command = [PFRESTObjectCommand fetchObjectCommandForObjectState:[object._state copy]
withSessionToken:sessionToken];
return [self _runFetchCommand:command forObject:object];
}] continueWithSuccessBlock:^id(BFTask *task) {
@strongify(self);
PFCommandResult *result = task.result;
return [self processFetchResultAsync:result.result forObject:object];
}] continueWithSuccessResult:object];
}
The call to [object _validateFetchAsync]
is a simple (sync) test on !self._state.objectId
so I don't see why there's a BFTask involved.
Even the change of queue is unnecessary as it will be done in the [self _runFetchCommand:command forObject:object];
call.
Does someone have any idea of the purpose of the first BFTask in:
- (BFTask *)fetchObjectAsync:(PFObject *)object withSessionToken:(NSString *)sessionToken { @weakify(self); return [[[[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ return [object _validateFetchAsync]; }] continueWithSuccessBlock:^id(BFTask *task) { @strongify(self); PFRESTCommand *command = [PFRESTObjectCommand fetchObjectCommandForObjectState:[object._state copy] withSessionToken:sessionToken]; return [self _runFetchCommand:command forObject:object]; }] continueWithSuccessBlock:^id(BFTask *task) { @strongify(self); PFCommandResult *result = task.result; return [self processFetchResultAsync:result.result forObject:object]; }] continueWithSuccessResult:object]; }
The call to
[object _validateFetchAsync]
is a simple (sync) test on!self._state.objectId
so I don't see why there's a BFTask involved. Even the change of queue is unnecessary as it will be done in the[self _runFetchCommand:command forObject:object];
call.
Maybe consistency? The SDK is big on BFTask. Or maybe there are historical reasons it's like that?
But I agree it seems like less fancy could do the same job.
This issue has been automatically marked as stale because it has not had recent activity. If you believe it should stay open, please let us know! As always, we encourage contributions, check out the Contributing Guide
I'm closing this as it seems to be resolved. Feel free to comment if you have any questions and we can re-open this issue.
Unfortunately it's not...
Fatal Exception: NSInvalidArgumentException
objectId should be non nil
Fatal Exception: NSInvalidArgumentException
0 CoreFoundation 0x182676d8c __exceptionPreprocess
1 libobjc.A.dylib 0x1818305ec objc_exception_throw
2 CoreFoundation 0x182676c6c -[NSException initWithCoder:]
3 Parse 0x101053cc8 +[PFRESTObjectCommand fetchObjectCommandForObjectState:withSessionToken:] + 21 (PFRESTObjectCommand.m:21)
4 Parse 0x10101ff8c __56-[PFObjectController fetchObjectAsync:withSessionToken:]_block_invoke.10 + 55 (PFObjectController.m:55)
5 Bolts 0x100b71f94 __62-[BFTask continueWithExecutor:successBlock:cancellationToken:]_block_invoke + 398 (BFTask.m:398)
6 Bolts 0x100b7198c __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 331 (BFTask.m:331)
7 Bolts 0x100b6f5ac __29+[BFExecutor defaultExecutor]_block_invoke_2 + 66 (BFExecutor.m:66)
8 Bolts 0x100b6fad8 -[BFExecutor execute:] + 131 (BFExecutor.m:131)
9 Bolts 0x100b7158c -[BFTask runContinuations] + 308 (BFTask.m:308)
10 Bolts 0x100b710f4 -[BFTask trySetResult:] + 247 (BFTask.m:247)
11 Bolts 0x100b725bc -[BFTaskCompletionSource setResult:] + 45 (BFTaskCompletionSource.m:45)
12 Bolts 0x100b71b90 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 + 340 (BFTask.m:340)
13 Bolts 0x100b71a40 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke + 348 (BFTask.m:348)
14 libdispatch.dylib 0x181f68aa0 _dispatch_call_block_and_release
15 libdispatch.dylib 0x181f68a60 _dispatch_client_callout
16 libdispatch.dylib 0x181f6fb84 _dispatch_queue_override_invoke$VARIANT$mp
17 libdispatch.dylib 0x181f75cac _dispatch_root_queue_drain
18 libdispatch.dylib 0x181f759fc _dispatch_worker_thread3
19 libsystem_pthread.dylib 0x18229bfac _pthread_wqthread
20 libsystem_pthread.dylib 0x18229bb08 start_wqthread
@SebC99 Could this be a threading issue in your code, as mentioned above? However, I think regardless of that, the SDK should not crash but the operation should fail with some error.
Hard to tell, but it doesn't seem so (@carbonimax ?) In any case, Parse SDK shouldn't throw an assert just after supposedly validating the value one line before ;)
@mtrezza Apparently there's no thread issue But we do log 0 length objectId values for those users (objectId: "") which seems very weird.
And as the SDK Code test for nil objectId only (here) it tries to start the RESTObjectCommand where the assert tests for 0 length objectId (here) Which then crash...
Any idea why the SDK would create "" values for objectId? Of course we don't have this kind of objectId in DB ;)
PS: we use the enableAutomaticUser
setting to allow anonymous parse users
we do log 0 length objectId values
To be exact, are you logging zero length String
values?
Can you go up the chain and log where these zero length String
values occur? Maybe fork the SDK and implement a log in the getter / setter method of objectId
and log the stack trace as well?
Yes string values being "" (length = 0)
Not really easy to do so in production (we can't reproduce the bug, but have thousands of users with it). We can try to find a way... but between the state.objectId, the localId, the self.objectId, and the method to create an object with an objectId, it seems there's a lot of way to set an objectId. We would need to implement a new setter to catch all in pfobject and in the state objects?
So after a lot of logging and testing, it appears the bug is for the _Installation class. For unknown reason, the PFInstallation persistent file doesn't include the objectId of that installation, which causes the fetch to fail.

If we try to detect it and save the PFInstallation.current()
instead of fetching it first, it kinda works... as it doesn't crash and succeeds to save on the server (even without the objectId, so I guess it uses the installation to find the right _Installation object). But even if the save on the server succeeds, the objectId is not set on the local PFInstallation object, and is still not saved on disk. So it will start again at the next launch and will never allow any fetch...
Any idea how we could set the objectId again? or why the save result objectId is not merged with the local object?
This issue has been automatically marked as stale because it has not had recent activity. If you believe it should stay open, please let us know! As always, we encourage contributions, check out the Contributing Guide
Reopening, issue not resolved.
This issue has been automatically marked as stale because it has not had recent activity. If you believe it should stay open, please let us know! As always, we encourage contributions, check out the Contributing Guide