Describe the bug
I am trying to save an item using DataStore and when the cloud syncs I get this error: 'cannot return null for non-nullable type: 'String' within parent 'User'' on all of the required fields in my model as well as some DataStore generated properties like __version and __lastChangedAt.
Amplify CLI Version
4.16.1
To Reproduce
Create a react app using create-react-app and bootstrap an amplify project using npx aws-amplify@latest. Then create the following graphql model:
type User @model {
id: ID!
firstName: String!
lastName: String!
}
Then call DataStore.save(new User({firstName: 'a first name', lastName: 'a last name'})) after generating the models and connecting to the cloud (amplify init, amplify push). Wait for the sync (and then another minute or so, due to a hanging loop?) and the error is thrown.
Expected behavior
A record should be made locally in the store and synced later to the cloud (the local record is indeed created, but the problem is the sync).
Screenshots
Location of error:
Odd return value from DataStore.save()
Desktop (please complete the following information):
Additional context
I really don't know what the problem is. I followed the DataStore documentation to a T! Initially I thought this was a developer error, but the fact that the DataStore generated fields are showing up in the error it makes me think that its likely a bug. The bug seems to show up about 30 seconds after I call DataStore.save(). Any help would be appreciated. I really like DataStore's promise but I'm not liking the delivery at the moment.
The error may be related to #5076 and #5080. In my setup I have social federation with user pools and I have been testing the various sign up methods while encountering this error.
How many times did you update the schema and pushed to the cloud? In my testing amplify doesn't remove older resolvers when you make update. Try the following
amplify remove api
amplify push
amplify add api
amplify push
or just create brand new amplify project with final schema. and only push once and don't make updates.
@apoorvmote I am not God and unfortunately do not have the foresight to create an immutable v99.9 api ;). I have updated the schema twice (once after removing the boilerplate 'Task' schema, and once more to add a couple of fields to my User model). But how would I know if amplify failed to remove old resolvers, and why do you think this is the cause?
What really gets me is that the return value from DataStore.save() has undefined values for the DataStore fields (i.e. __version, __lastChangedAt, __deleted). And then once the data store tries to sync, it fails (perhaps on an unrelated cause).
But how would I know if amplify failed to remove old resolvers, and why do you think this is the cause?
I am using Datastore and basic schema like
type User @model {
id: ID!
firstName: String!
lastName: String!
}
should work without any problems.
However I stoppted using amplify-app@latest
becuase it doesn't update .gitignore
and I am using multi env setup that made problem worse.
I create app normally and update api to change conflict resolution to optimistic concurrency. Then start using datastore.
@apoorvmote I tried your solution and it did not work. I'm sorry to say that it would not be helpful in the long term anyways. I must be able to add change the api as the app evolves.
I found a related issue in the ios repo. The similarity is that the store is not syncing to the cloud and an error is thrown about the DataStore related fields. None of the solutions mentioned in that issue worked for me. I can confirm that DataStore is not syncing to the cloud in my case, despite the fact that my api is configured with a conflict detection method (automerge in my case). SOS aws! I really want to use this feature as it immensely simplifies my development, but I may have to move on and wait for v2 :/
@karrettgelley change conflic dectation to optimistic concurrency then it will work.
@apoorvmote I prefer the automerge strategy. I shouldn't have to strange strategies to get the module to work correctly. Slapping the side of the tv to get it to focus doesn't fix the problem. There seems to be a pretty big bug with DataStore at the moment, as there are many issues related to this post. I'm hoping someone on the team will respond to this soon
@karrettgelley Do you think you could share a gist of your code and GraphQL schema so that we could try to reproduce? Looking through the details of this post it seems there is some inconsistency between the model generation and fields that are available in your backend. The only guess I have is that there might be a workflow during your data model iteration that is causing the problem but it's not apparent to me from the information here so far what that might be.
@undefobj I created a repo that demonstrates the DataStore syncing error. Take a look at the readme for specific instructions, or pull the repo and try it out. It should have all the credentials needed to run it if you pull it (i'll redact when this issue is resolved). Thank you for your help! I saw your talk on DataStore and loved it. It's a gamechanger for my app, just gotta get it workin
@karrettgelley I did get same error you got at #5130
Issue was I had 2 different project who had exact same schema and I ran one after another on same localhost. First one worked and 2nd one crashed.
So I opened incognito window and opened 2nd one that crashed and it worked fine.
So try with incognito window.
And no matter how hard I try to start project on incognito window. The project opens in regular window & crashes. Then I go to incognito window and refresh. It is very annoying.
I had lots of issues because I was running many different project at localhost. Assuming you are using Chrome for testing. Make sure to go into settings and remove all data for localhost from time to time. Especially if you are getting error. Or like I said use incognito. But switching to incognito is annoying every time.
From google search there used to be setting for developers to automatically clear localhost data. I could not find it. So I had to manually clean everything for localhost and restart the browser.
Hi @karrettgelley
I cloned your repo and noticed that Amplify.configure()
is not being called, try adding this to your App.js
import { default as Amplify } from "@aws-amplify/core";
import { default as awsConfig } from "./aws-exports";
Amplify.configure(awsConfig);
Also, looks like you might need to generate a new API_KEY since the one in your repo seems to be expired.
Edit:
Can you make sure that your API has the @model types with _version
field in them? You can verify this in the Schema section of the appsync console (you can run amplify api console
to open it in a browser window)
@manueliglesias I checked the API and _version is indeed there. I'll fix the configuration and update the repo, but how do I update the api key? I tried setting CreateAPIKey to 0, but I still get an unauthorized exception.
Edit: Figured out how to update the api key. I also updated the repo by adding Amplify.configure() but the problem still persists.
Edit2: I am using DataStore 1.0.8 and I notice there is an update to 1.1.0. Does this update address the syncing issue?
@manueliglesias I checked the API and _version is indeed there. I'll fix the configuration and update the repo, but how do I update the api key? I tried setting CreateAPIKey to 0, but I still get an unauthorized exception.
Edit: Figured out how to update the api key. I also updated the repo by adding Amplify.configure() but the problem still persists.
Edit2: I am using DataStore 1.0.8 and I notice there is an update to 1.1.0. Does this update address the syncing issue?
@karrettgelley I've sent a PR
https://github.com/karrettgelley/datastore-demo/pull/1
It is doing a DataStore.observe on useEffect to kick off the sync on component mount. Let me know if that works for you
@manueliglesias Tested it and it works. Can you explain this wizardry? Also, did you mean to use useEffect, or useState? In the PR you call useState
@karrettgelley oops, yeah, it should be useEffect
馃槄
The explanation is that by doing a DataStore.observe()
(or e.g. a DataStore.query()
) on app mount, it kicks off the sync mechanism. Without this, if your first operation against the DataStore is a save, it might not get executed. This is a bug that we are tracking, so my PR is mostly a workaround to make DataStore start the sync as soon as possible so it is ready to accept saves later.
I am going to close this issue, feel free to open a new one if you encounter more issues or have more questions.
@manueliglesias Thank you very much! Where can I track the bug?
@manueliglesias Is there any update on this bug? Do we still need to use the workaround?
Hi Karrett,
I'm new to datastore and I seem to have the same issue, it could be my lack of experience also I will admit. I made a basic project following a Nader Dabit video and then added datastore with the auto merge selected and I also got these 3 new _fields added to my queries and mutations. Is this the same problem you are having? I was hoping not to have to start a new project again, will try the optimistic option and see, thank you.
Re: cannot return null on a nullable property.
I noticed that on updates/deletes. If I do not return the updated property especially if the update mutation call was executed from cloud (aws console), the error happens. Datastore automatically skipped the incoming subscription payload for me because of the none nullable null issue.
What worked for me was to return the updated property. Or just return all property. Btw, like any exploring programmer, I did a lot of amplify push. I did not resolve to removing my api at all
e.g.
type Post @model {
id: ID!
name: String!
team: String
}
---mutation call somewhere
updatePost ({
id: "123sadwsfsdkljgdflgk-sdfjsdlfkj"
_version: 1
name: "New Name"
}){
id
_version
_delete
_lastUpdatedAt
name
}
I've been having the same issue and @mcringor 's suggestion resolves it for me too.
I was performing a mutation from a lambda.
diff --git a/amplify/backend/function/updateTask/src/api.js b/amplify/backend/function/updateTask/src/api.js
index 37ac075..2571242 100644
--- a/amplify/backend/function/updateTask/src/api.js
+++ b/amplify/backend/function/updateTask/src/api.js
@@ -65,7 +65,12 @@ const UPDATE_TASK = gql`
$condition: ModelTaskConditionInput
) {
updateTask(input: $input, condition: $condition) {
+ id
status
+ ownerId
+ target
+ _version
+ _lastChangedAt
}
}
`;
I guess Datastore is simply subscribing to createModel
, updateModel
etc and expecting that developers will always use the mutations provided by codegen (which return all the fields) / just know the internals of DataStore?
This should be re-opened / re-created as a bug, right?
Most helpful comment
Re: cannot return null on a nullable property.
I noticed that on updates/deletes. If I do not return the updated property especially if the update mutation call was executed from cloud (aws console), the error happens. Datastore automatically skipped the incoming subscription payload for me because of the none nullable null issue.
What worked for me was to return the updated property. Or just return all property. Btw, like any exploring programmer, I did a lot of amplify push. I did not resolve to removing my api at all
e.g.
type Post @model {
id: ID!
name: String!
team: String
}
---mutation call somewhere
updatePost ({
id: "123sadwsfsdkljgdflgk-sdfjsdlfkj"
_version: 1
name: "New Name"
}){
id
_version
_delete
_lastUpdatedAt
name
}