Skip to content

Error: Attempted to change an objectId to one that's already known to the Offline Store #1202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Robtles opened this issue Oct 26, 2017 · 22 comments

Comments

@Robtles
Copy link

Robtles commented Oct 26, 2017

Hi everyone,

I'm having a regular issue with my project, using Parse Server. First, I call a Parse Cloud function to populate a user's data list :

var dataSet: Set<MyData>?

func loadData(withParameters parameters: [String : Any]) {

        PFCloud.callFunction(inBackground: "loadData", withParameters: parameters) { (success, error) in

            if let objects = success as? [[String : Any]] {

                let dataTable: [MyData] = objects.map({ (object) -> MyData in

                    let myData = MyData(dataSource: PFObject(className: "MyData", 
                                        dictionary: object))
                    myData.dataSource?.objectId = object["objectId"] as? String
                    return myData
                })

                if self.dataSet == nil {
                    self.dataSet = []
                }

                self.dataSet = Set(dataTable)
            }
      }
} 

On the code mentioned above, I have to set the objectId because without this, no matter how many objects I fetch from Parse, when I reduce the array to a set with the last instruction I end up with only one object in it.

However, although this works, when I call this function again to update user's data, I get this error on the myData.dataSource?.objectId = temp["objectId"] as? String line :

Attempted to change an objectId to one that's already known to the Offline Store.

Any idea? Thanks for your help.

@iThinkersTeam
Copy link

Have same problem :(

@flovilmart
Copy link
Contributor

@StratRob when calling a cloud function, the objects should already by hydrated as PFObject’s. Can you confirm that they are indeed not dictionaries? PFObject can be casted as dictionaries as they also conform to subscripting extensions.

@ceramicatheist
Copy link
Contributor

I can confirm we got a new flood of this exact crash when deploying a new version of our app that upgraded from 1.15.3 to 1.15.4, with the associated Bolts upgrade, with no app changes that should have triggered something like this.
(apologies for the long stack trace...)

Attempted to change an objectId to one that's already known to the OfflineStore

Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x181306364 __exceptionPreprocess
1  libobjc.A.dylib                0x18054c528 objc_exception_throw
2  CoreFoundation                 0x1813062ac -[NSException initWithCoder:]
3  Hidrate                        0x100966d20 -[PFOfflineStore updateObjectIdForObject:oldObjectId:newObjectId:] (PFOfflineStore.m:1027)
4  Hidrate                        0x1008fcb40 -[PFObject _notifyObjectIdChangedFrom:toObjectId:] (PFObject.m:1773)
5  Hidrate                        0x1008fc738 -[PFObject set_state:] (PFObject.m:1729)
6  Hidrate                        0x1008f92e4 -[PFObject(Private) _mergeFromServerWithResult:decoder:completeData:] (PFObject.m:1275)
7  Hidrate                        0x1008f9128 -[PFObject(Private) _mergeAfterSaveWithResult:decoder:] (PFObject.m:1268)
8  Hidrate                        0x1008f9abc __43-[PFObject(Private) handleSaveResultAsync:]_block_invoke (PFObject.m:1332)
9  Hidrate                        0x1008c2860 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331)
10 Hidrate                        0x1008c88b0 __29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66)
11 Hidrate                        0x1008c8e20 -[BFExecutor execute:] (BFExecutor.m:131)
12 Hidrate                        0x1008c26c0 -[BFTask continueWithExecutor:block:cancellationToken:] (BFTask.m:368)
13 Hidrate                        0x1008c2558 -[BFTask continueWithExecutor:withBlock:] (BFTask.m:316)
14 Hidrate                        0x1008c1e30 +[BFTask taskFromExecutor:withBlock:] (BFTask.m:227)
15 Hidrate                        0x1008f98b0 -[PFObject(Private) handleSaveResultAsync:] (PFObject.m:1327)
16 Hidrate                        0x10090ac64 __79-[PFPinningEventuallyQueue _didFinishRunningCommand:withIdentifier:resultTask:]_block_invoke.115 (PFPinningEventuallyQueue.m:232)
17 Hidrate                        0x1008c2860 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331)
18 Hidrate                        0x1008c88b0 __29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66)
19 Hidrate                        0x1008c8e20 -[BFExecutor execute:] (BFExecutor.m:131)
20 Hidrate                        0x1008c26c0 -[BFTask continueWithExecutor:block:cancellationToken:] (BFTask.m:368)
21 Hidrate                        0x1008c2c34 -[BFTask continueWithBlock:] (BFTask.m:375)
22 Hidrate                        0x10090a97c __79-[PFPinningEventuallyQueue _didFinishRunningCommand:withIdentifier:resultTask:]_block_invoke (PFPinningEventuallyQueue.m:231)
23 Hidrate                        0x1008c2860 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331)
24 Hidrate                        0x1008c88b0 __29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66)
25 Hidrate                        0x1008c8e20 -[BFExecutor execute:] (BFExecutor.m:131)
26 Hidrate                        0x1008c244c -[BFTask runContinuations] (BFTask.m:308)
27 Hidrate                        0x1008c1f94 -[BFTask trySetResult:] (BFTask.m:247)
28 Hidrate                        0x1008bf358 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45)
29 Hidrate                        0x1008c2934 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:354)
30 Hidrate                        0x1008c88b0 __29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66)
31 Hidrate                        0x1008c8e20 -[BFExecutor execute:] (BFExecutor.m:131)
32 Hidrate                        0x1008c244c -[BFTask runContinuations] (BFTask.m:308)
33 Hidrate                        0x1008c1f94 -[BFTask trySetResult:] (BFTask.m:247)
34 Hidrate                        0x1008bf358 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45)
35 Hidrate                        0x1008c2a70 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340)
36 Hidrate                        0x1008c2860 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331)
37 Hidrate                        0x1008c88b0 __29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66)
38 Hidrate                        0x1008c8e20 -[BFExecutor execute:] (BFExecutor.m:131)
39 Hidrate                        0x1008c244c -[BFTask runContinuations] (BFTask.m:308)
40 Hidrate                        0x1008c1f94 -[BFTask trySetResult:] (BFTask.m:247)
41 Hidrate                        0x1008bf358 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45)
42 Hidrate                        0x1008c2a70 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340)
43 Hidrate                        0x1008c2860 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331)
44 Hidrate                        0x1008c88b0 __29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66)
45 Hidrate                        0x1008c8e20 -[BFExecutor execute:] (BFExecutor.m:131)
46 Hidrate                        0x1008c244c -[BFTask runContinuations] (BFTask.m:308)
47 Hidrate                        0x1008c1f94 -[BFTask trySetResult:] (BFTask.m:247)
48 Hidrate                        0x1008bf358 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45)
49 Hidrate                        0x1008c2a70 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340)
50 Hidrate                        0x1008c2860 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331)
51 Hidrate                        0x1008c88b0 __29+[BFExecutor defaultExecutor]_block_invoke_2 (BFExecutor.m:66)
52 Hidrate                        0x1008c8e20 -[BFExecutor execute:] (BFExecutor.m:131)
53 Hidrate                        0x1008c244c -[BFTask runContinuations] (BFTask.m:308)
54 Hidrate                        0x1008c1f94 -[BFTask trySetResult:] (BFTask.m:247)
55 Hidrate                        0x1008bf358 -[BFTaskCompletionSource setResult:] (BFTaskCompletionSource.m:45)
56 Hidrate                        0x1008c2a70 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke_2 (BFTask.m:340)
57 Hidrate                        0x1008c2918 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:348)
58 libdispatch.dylib              0x180c82a14 _dispatch_client_callout
59 libdispatch.dylib              0x180cc55ec _dispatch_sync_invoke_and_complete_recurse
60 libdispatch.dylib              0x180cc50f8 _dispatch_sync_wait
61 Hidrate                        0x100936f00 PFThreadsafetySafeDispatchSync (PFThreadsafety.m:31)
62 libdispatch.dylib              0x180c82a54 _dispatch_call_block_and_release
63 libdispatch.dylib              0x180c82a14 _dispatch_client_callout
64 libdispatch.dylib              0x180cbe394 _dispatch_queue_override_invoke$VARIANT$armv81
65 libdispatch.dylib              0x180cc42a4 _dispatch_root_queue_drain
66 libdispatch.dylib              0x180cc4008 _dispatch_worker_thread3
67 libsystem_pthread.dylib        0x180f2b06c _pthread_wqthread
68 libsystem_pthread.dylib        0x180f2ab6c start_wqthread

Thanks to the Bolts dispatching strategy it's hard to see where in our app this is actually happening, but as we use no cloud functions I don't think it's specific to them.

Obviously not enough info for a real diagnosis, but what's your feeling? Might Bolts have changed something bad that screws up the order of some operations?

@flovilmart
Copy link
Contributor

Bolts doesn’t swallow exceptions anymore, and I believe this is the reason why we see those now, i’ll Check with an older version if those were properly swallowed before

@flovilmart
Copy link
Contributor

I can confirm that all PFConsistencyAssertion were previously caught in Bolts 1.8.* and now bolts don't catch them anymore.

We need to properly refactor those code paths to transofrm exceptions into errors which is problematic as it stands.

@flovilmart
Copy link
Contributor

@StratRob , @ceramicatheist after much work into putting the SDK on a stable version for the Bolts 1.9.0, this exception is gonna stay, as you're mutating objects from the local datastore.

What's happening, is that you're changing the objectId from a known object, in previous versions this would yield an NSInternalInconsistencyException that would be caught by the bolts tasks runner.

This is an important error that should not be disregarded.

@ceramicatheist would you be able to share some code that would be responsible for this? Do you ever set manually an objectId on an objectWithoutData?

@ceramicatheist
Copy link
Contributor

I did a sweep of our code looking for objectID, and we don't set it anywhere. We do extensively use the local store, but never assign an objectID.
(I do make a single dummy PFObject with a dummy ObjectID, but it's only used to construct a "must-fail" query and it never touches the local store.)

I should add (and maybe put in a separate issue?) that this was not the worst of the new crashes we saw. The most frequent was:

Fatal Exception: NSInternalInconsistencyException
User cannot be saved unless they are already signed up. Call signUp first.
0  CoreFoundation                 0x181fa9d04 __exceptionPreprocess
1  libobjc.A.dylib                0x1811f8528 objc_exception_throw
2  CoreFoundation                 0x181fa9c4c -[NSException initWithCoder:]
3  Hidrate                        0x10277ea8c -[PFUser(Private) _checkSaveParametersWithCurrentUser:] (PFUser.m:142)
4  Hidrate                        0x10272fd44 __81+[PFObject(Private) _deepSaveAsyncChildrenOfObject:withCurrentUser:sessionToken:]_block_invoke_3 (PFObject.m:465)
5  Hidrate                        0x1026fe860 __55-[BFTask continueWithExecutor:block:cancellationToken:]_block_invoke (BFTask.m:331)
6  libdispatch.dylib              0x18192d088 _dispatch_call_block_and_release
7  libdispatch.dylib              0x18192d048 _dispatch_client_callout
8  libdispatch.dylib              0x181934090 _dispatch_queue_override_invoke$VARIANT$mp
9  libdispatch.dylib              0x18193a1c8 _dispatch_root_queue_drain
10 libdispatch.dylib              0x181939f10 _dispatch_worker_thread3
11 libsystem_pthread.dylib        0x181bd3120 _pthread_wqthread
12 libsystem_pthread.dylib        0x181bd2c20 start_wqthread

Which would occur when we sign up a new user authenticating with Facebook.

In the completion block of PFFacebookUtils.logInInBackground(withReadPermissions:), we immediately set some properties on the new User object and attempt to save it, and reliably get this exception.

Postponing that save with an async dispatch was enough to work around that particular problem.

I'm sorry, I wish I had nice succinct code snippets to share with you. The way Bolts works, it's very hard to tell which bit of our app is even at fault.

@flovilmart
Copy link
Contributor

Ok, if you reliably have that error can you check the latest cocoapods version? 1.17.0-alpha.2. You should lot have the exception but a soft error

@flovilmart
Copy link
Contributor

@StratRob I have been looking in depth into your issue, and I believe I found the solution.
When the localDatastore is enabled, you shoud always use [PFObject objectWithoutData:className objectId:objectId] to try to prefetch the object from the store in memory cache.

Your app is crashing because the new instances of PFObject's you're subsequently creating break the 1..1 mapping for the offline in memory cache. There's a potential fix, by changing the implementation of [PFObject className:@"" dictionary:@{}].

on another note, the objects in response to a PFCloud.runFunction call should contain the PFObjects, and I'M more enclined making sure that's the case. Can you double check that it isn't the case?

@ceramicatheist any chance you can isolate the call that yields that issue on merge?

@flovilmart
Copy link
Contributor

@StratRob after further invesigation, I can confirm that if CloudCode returns proper Parse Object's they are already exposed as-is in the results. You don't need your initializer.

var dataSet: Set<MyData>?

func loadData(withParameters parameters: [String : Any]) {

        PFCloud.callFunction(inBackground: "loadData", withParameters: parameters) { (success, error) in

            if let objects = success as? [PFObject] {

                let dataTable: [MyData] = objects.map({ (object) -> MyData in

                    return MyData(dataSource: object)
                })

                if self.dataSet == nil {
                    self.dataSet = []
                }

                self.dataSet = Set(dataTable)
            }
      }
} 

This code should work. I'll be closing the issue now.

@ceramicatheist @iThinkersTeam I encourgage both of you to open a new issue.

@ananfang
Copy link

ananfang commented Feb 5, 2018

@flovilmart
We've also encountered this issue.
I tried using the latest cocoapod version (which is 1.17.0-alpha.6), it will change ParseLiveQuery version (back to 1.x, the version still depend on SocketRocket, not Starscream) and conflict with the new Bolts API...

@flovilmart
Copy link
Contributor

flovilmart commented Feb 5, 2018

@ananfang what issue do you mean? You can use LiveQuery on branch 'release-2.4.0' (parse-community/ParseLiveQuery-iOS-OSX#151) it's compatible with the latest Parse SDK alpha

@ananfang
Copy link

ananfang commented Feb 5, 2018

@flovilmart
I just save a parse object included an array of pointers of other objects, it crash the app and show error: "Attempted to change an objectId to one that's already known to the Offline Store"

After changing Parse to branch 'release-1.17.0' and ParseLiveQuery to branch 'release-2.4.0', it will not crash the app, thanks for your effort.

@flovilmart
Copy link
Contributor

@ananfang it doesn,t crash anymore but the operation errors :)

@ananfang
Copy link

ananfang commented Feb 6, 2018

@flovilmart
After testing many times, this operation error still crash the app, not always, but about 50% to occur this issue.

The error message shows that:
Attempted to change an objectId to one that's already known to the OfflineStore. className: Story old: (null), new: 45le7HIrGE

Followings are my codes to save a Parse object with class name Story:

let story = Story() // Story is a PFObject subclass.
story.acl = /* Set ACL */
story.year = /* Set year */
story.month = /* Set month */
story.day = /* Set day */
story.locale = /* Set locale */

// Crash at follow line.
story.saveInBackground().continueWith(executor: BFExecutor.mainThread(), block: { task in
  // Never enter this block.
})

There is a Live Query listen to this story (created, updated, entered and deleted)

@ananfang
Copy link

ananfang commented Feb 6, 2018

@flovilmart
I found there are two macros in PFAssert.h (branch release-1.17.0):
PFPreconditionBailAndSetError and PFPreconditionBailOnError

Should be
PFPreconditionFailAndSetError and PFPreconditionFailOnError

@ananfang
Copy link

ananfang commented Feb 6, 2018

@flovilmart
I've just sent a pull request to branch release-1.17.0 about this issue.

In the function - (void)updateObjectIdForObject:oldObjectId:newObjectId: of PFOfflineStore.m, if oldObjectId is nil but createdAt is not nil, means this object is just saved, and should update its reference in the local store without exception.

flovilmart pushed a commit that referenced this issue Feb 6, 2018
* 1. Fix typo PFPreconditionFailOnError and PFPreconditionFailAndSetError. 2. If there is a new object in OfflineStore, should not send the exception when the saved object update its OfflineStore cache

* Change the condition about newly saved object

* fix typo

* Revert to PFPreconditionBailOnError and PFPreconditionBailAndSetError
@ceramicatheist
Copy link
Contributor

Hooray, I look forward to trying this out in our app and finally advancing from 1.15.3.

Are pre-built frameworks no longer available?

@flovilmart
Copy link
Contributor

They should be

@ceramicatheist
Copy link
Contributor

ceramicatheist commented May 29, 2018 via email

@flovilmart
Copy link
Contributor

Nope, you’re right my bad. They were supposedly automated, but it seems that it didn’t work as expected

@Robtles
Copy link
Author

Robtles commented Sep 6, 2018

Coming back a little bit late, but thank you very much for the help and explanation!

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

No branches or pull requests

5 participants