'NoneType' object has no attribute 'system'"
Daniyaldehleh opened this issue · 34 comments
Hi All!
I am trying to retrieve access token by calling handler_input.request_envelope.context.system.user.access_token
, however; I am getting
{
"errorMessage": "'NoneType' object has no attribute 'system'",
"errorType": "AttributeError",
"stackTrace": [
" File \"/opt/python/ask_sdk_core/skill_builder.py\", line 111, in wrapper\n response_envelope = skill.invoke(\n",
" File \"/opt/python/ask_sdk_core/skill.py\", line 181, in invoke\n api_token = request_envelope.context.system.api_access_token\n"
]
}
class GoogleIntentHandler(AbstractRequestHandler):
def can_handle(self, handler_input):
return ask_utils.is_intent_name("GoogleIntent")(handler_input)
def handle(self, handler_input):
#LOP = request.Session.User.AccessToken
accessToken = handler_input.request_envelope.context.system.user.access_token
global service
service = build('calendar', 'v3', credentials=accessToken)
event = {
'summary': 'Test',
'location': 'At home',
'description': 'A chance to hear more about Google\'s developer products.',
'start': {
'dateTime': '2020-07-29T12:12:10',
'timeZone': 'America/Los_Angeles',
},
'end': {
'dateTime': '2020-07-29T12:32:47',
'timeZone': 'America/Los_Angeles',
}
}
#check if you can call a color via the api too
event = service.events().insert(calendarId='primary', body=event).execute()
return (
handler_input.response_builder
.speak(speak_output)
)
I believe it's a capital "S" in "System".
handler_input.request_envelope.context.System.user.access_token
@timothyaaron I did try changing it to capital S. Unfortunately, nothing happened.
Can you elaborate on "nothing happened"? 🙂
Sorry, I just saved it again and test it and got:
"Input should be a RequestHandler instance"
. Looks like we are making progress :)
🤔 …that seems… odd. Are you sure it didn't say "'NoneType' object has no attribute 'System'"
?
@timothyaaron exactly! it seemed odd! cause it should have said "System". Fortunately, after the 2nd save&test it got updated.
@timothyaaron Do you know what is meant by that error?
I haven't ever gotten that error. It would be helpful if you knew what line that error was generated on, and if you could provide it, but…
…if I had to guess I'd say the can_handle
or handle
function (or, even more likely, something further up the chain) isn't being passed a valid Request. 🤷♂️
Except that error appears to be generated after your access_token
line. 🤔
And the only SDK thing you interact with following that is your return
, so… wait… where do you define speak_output
?
Oh. Got it.
handler_input.response_builder.speak(speak_output)
return handler_input.response_builder.response
@timothyaaron Thanks for looking into it. I pasted your code, however, this time after multiple save&test, I was faced with the same error. Input should be a RequestHandler instance
That was definitely an error, so keep that change.
I did a little digging. That error is returned in what's probably named lambda_function.py
or similar (your entry point) on your .add_request_handler()
line. The object being passed in isn't inheriting AbstractRequestHandler
(which GoogleIntentHandler
appears to be doing… so maybe a different one?).
Or maybe you're not instantiating it (make sure the ()
comes after GoogleIntentHandler
).
i.e., sb.add_request_handler(GoogleIntentHandler())
@timothyaaron Thanks for looking it up.
I realized that I am actually stuck with the
{
"errorMessage": "'NoneType' object has no attribute 'system'",
"errorType": "AttributeError",
"stackTrace": [
" File \"/opt/python/ask_sdk_core/skill_builder.py\", line 111, in wrapper\n response_envelope = skill.invoke(\n",
" File \"/opt/python/ask_sdk_core/skill.py\", line 181, in invoke\n api_token = request_envelope.context.system.api_access_token\n"
]
}
and the reason it bypassed it last time and was giving that error was because it faced a new error after adding a new class. Thus, once I removed the class, I realize the "'NoneType' object has no attribute 'system'"
error didn't actually go. Could it be due to an internal error? cause it is referring to skill_builder.py & skill.py file and not my lambda_function.py file.
Update: I posted the error on SO and look like, it is an internal error with the ask_sdk_core
A couple notes...
- I was wrong. The raw JSON uses
System
, the SDK usessystem
. - I think you misunderstood the SO comment if you assumed it was an SDK error. Specifically, "That suggests you're using the library incorrectly and/or passing bad data somewhere".
- You are correct in that somehow
request_envelope.context
is aNoneType
. That's getting build directly from your incomingevent
.context
is a required object, so it should be there. Can you post your entire incoming request?
@danieldhz - it might not solve the problem you're having, but since you're using one util function already, I thought I'd point out there is one that gets the access token - get_account_linking_access_token
Depending on the code path, it might be that the object being passed into the handler is not the proper type. I've been able to work around that using something like this: request_envelope['context'] instead of the attribute notation.
I ran into a similar problem with caching product details as described in this blog. You'd have to adapt it to the request_envelope or whichever variable is the trouble spot.
@timothyaaron thanks for your constructive reply :)
I read the "..library incorrectly and/or passing bad data somewhere". But the thing is I am not even using any special libs in the class nor in the whole app to give such error. In fact, I have literally copy-pasted the same code to dev console, and it worked completely fine even after calling the GoogleIntentHandler. However, unfortunately, I can't use the dev console because I have to use external libs. Thus, I am completely confused on what's going on :(
@franklin-lobb thanks for pointing out. I will definitely keep that in mind in case we get there. However, even when I completely comment out the access token class, I am still left with the same error. On the other hand, when I paste the same code to the dev console it works completely fine even with access token class being on. Thus, lambda makes sense to be at fault as it's the only difference. However, I am not sure how I shall tweak lambda as I literally installed the libs and uploaded to lambda's layers.
by dev console, do you mean the Lambda console, or the Alexa console?
I experienced a situation previously (it was a while ago, and I don't recall if it was the same issue around the deserialization), however I could get the code to run in the Lambda console with a test event, but when I invoked the function via the source event, it wouldn't run, but modifying from the attribute notation (.context) to key notation (['context']) allowed it to work (and then I don't think it worked in the console).
@franklin-lobb Sorry, I meant Alexa dev console. The same code works in alexa dev console but not lambda. The thing is the request_envelope is already commented out and I am getting the same error. I tried adding
google-api-core==1.22.0
google-api-python-client==1.10.0
google-auth==1.19.2
google-auth-httplib2==0.0.4
google-auth-oauthlib==0.4.1
googleapis-common-protos==1.52.0
to the requirement.txt to import the packages into dev console, however; I got:
The 'google-api-core' distribution was not found and is required by the application Traceback
Hey @danieldhz , how are you testing the code? If you are using the test console on AWS Lambda, what is your request envelope JSON? If you are testing it through invoking the skill/intent, can you paste in the exact request envelope JSON that is being passed?
Is anyone else amused by their "helper functions"?
ask_sdk_core.utils.request_util.get_account_linking_access_token(handler_input)
handler_input.request_envelope.context.system.user.access_token
It literally just returns that line. You should already know what's in the request object. It's shorter and more explicit to just get what you need from where it actually exists in most cases vs memorizing longer "helper functions" that have to be imported.
Anyways… 🤔 …that's weird about running in the console. Now it's starting to sound like your code isn't being deployed properly. Do you get the error if you run it locally? (If you're comfortable sharing—repo>settings>manage_access>invite, I'm happy to spend a couple minutes digging into it.)
Hi Guys!
Thanks for all of your participation to make this work :)
As you guys mentioned, I realized that I wasn't testing the right JSON input. Therefore, I am really sorry for my testing immaturity. If I didn't have you I would've been scratching my head all day. Hopefully, one day I will be as good as you guys. Nevertheless, after testing with the right JSON, I realized the actual error is:
Input:
{
"version": "1.0",
"session": {
"new": false,
"sessionId": "amzn1.echo-api.session.ae83d982-4043-4371-a9d9-9aa39f85d79d",
"application": {
"applicationId": "amzn1.ask.skill.a8bac98c-dfbd-4caa-ae48-42e4cdad4f4e"
},
"user": {
"userId": "amzn1.ask.account...",
"permissions": {
"consentToken": "eyJ0eXAiOiJKV..."
}
}
},
"context": {
"Viewports": [
{
"type": "APL",
"id": "main",
"shape": "RECTANGLE",
"dpi": 160,
"presentationType": "STANDARD",
"canRotate": false,
"configuration": {
"current": {
"video": {
"codecs": [
"H_264_42",
"H_264_41"
]
},
"size": {
"type": "DISCRETE",
"pixelWidth": 1024,
"pixelHeight": 600
}
}
}
}
],
"Viewport": {
"experiences": [
{
"arcMinuteWidth": 246,
"arcMinuteHeight": 144,
"canRotate": false,
"canResize": false
}
],
"shape": "RECTANGLE",
"pixelWidth": 1024,
"pixelHeight": 600,
"dpi": 160,
"currentPixelWidth": 1024,
"currentPixelHeight": 600,
"touch": [
"SINGLE"
],
"video": {
"codecs": [
"H_264_42",
"H_264_41"
]
}
},
"System": {
"application": {
"applicationId": "amzn1.ask.skill.a8bac98c-dfbd-4caa-ae48-42e4cdad"
},
"user": {
"userId": "amzn1.ask.accou..",
"permissions": {
"consentToken": "eyJ0eXAiOiJKV1QiL.."
}
},
"device": {
"deviceId": "amzn1.ask.device.AGAN7ZCSHC7..",
"supportedInterfaces": {}
},
"apiEndpoint": "https://api.amazonalexa.com",
"apiAccessToken": "eyJ0e."
}
},
"request": {
"type": "IntentRequest",
"requestId": "amzn1.echo-api.request.9caeb4cd-6ff5-4115-9d41-a4a87f",
"locale": "en-US",
"timestamp": "2020-08-11T15:14:11Z",
"intent": {
"name": "GoogleIntent",
"confirmationStatus": "NONE"
}
}
}
Output:
{
"version": "1.0",
"sessionAttributes": {},
"userAgent": "ask-python/1.14.0 Python/3.8.4",
"response": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>Sorry, I had trouble doing what you asked. Please try again.</speak>"
},
"reprompt": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>Sorry, I had trouble doing what you asked. Please try again.</speak>"
}
},
"shouldEndSession": false
}
}
Function logs:
[WARNING] file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth
Traceback (most recent call last):
File "/opt/python/googleapiclient/discovery_cache/file_cache.py", line 33, in <module>
from oauth2client.contrib.locked_file import LockedFile
ModuleNotFoundError: No module named 'oauth2client'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/opt/python/googleapiclient/discovery_cache/file_cache.py", line 37, in <module>
from oauth2client.locked_file import LockedFile
ModuleNotFoundError: No module named 'oauth2client'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/opt/python/googleapiclient/discovery_cache/__init__.py", line 44, in autodetect
from . import file_cache
File "/opt/python/googleapiclient/discovery_cache/file_cache.py", line 40, in <module>
raise ImportError(
ImportError: file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth[WARNING] 2020-08-11T15:14:26.911Z d2b79235-c1ca-4b96-9782-a17058ea5e0e Compute Engine Metadata server unavailable onattempt 1 of 3. Reason: [Errno 111] Connection refused
[WARNING] 2020-08-11T15:14:27.935Z d2b79235-c1ca-4b96-9782-a17058ea5e0e Compute Engine Metadata server unavailable onattempt 2 of 3. Reason: [Errno 111] Connection refused
[WARNING] 2020-08-11T15:14:28.959Z d2b79235-c1ca-4b96-9782-a17058ea5e0e Compute Engine Metadata server unavailable onattempt 3 of 3. Reason: [Errno 111] Connection refused
[WARNING] 2020-08-11T15:14:28.959Z d2b79235-c1ca-4b96-9782-a17058ea5e0e Authentication failed using Compute Engine authentication due to unavailable metadata server.
[ERROR] 2020-08-11T15:14:28.959Z d2b79235-c1ca-4b96-9782-a17058ea5e0e Could not automatically determine credentials. Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and re-run the application. For more information, please see https://cloud.google.com/docs/authentication/getting-started
Traceback (most recent call last):
File "/opt/python/ask_sdk_runtime/dispatch.py", line 118, in dispatch
output = self.__dispatch_request(handler_input) # type: Union[Output, None]
File "/opt/python/ask_sdk_runtime/dispatch.py", line 182, in __dispatch_request
output = supported_handler_adapter.execute(
File "/opt/python/ask_sdk_runtime/dispatch_components/request_components.py", line 437, in execute
return handler.handle(handler_input)
File "/var/task/lambda_function.py", line 104, in handle
service = build('calendar', 'v3', credentials=accessToken)
File "/opt/python/googleapiclient/_helpers.py", line 134, in positional_wrapper
return wrapped(*args, **kwargs)
File "/opt/python/googleapiclient/discovery.py", line 252, in build
return build_from_document(
File "/opt/python/googleapiclient/_helpers.py", line 134, in positional_wrapper
return wrapped(*args, **kwargs)
File "/opt/python/googleapiclient/discovery.py", line 431, in build_from_document
credentials = _auth.default_credentials()
File "/opt/python/googleapiclient/_auth.py", line 44, in default_credentials
credentials, _ = google.auth.default()
File "/opt/python/google/auth/_default.py", line 354, in default
raise exceptions.DefaultCredentialsError(_HELP_MESSAGE)
google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials. Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and re-run the application.
I realized that someone else literally fell in the same problem SO. I believe in plain English, the error is saying that it can't store the access token as lambda is stateless? and numerous errors about libs. if so, could just storing it in a persistent variable do the job?
@timothyaaron Repo
Hey @danieldhz , thanks for the update. Glad to know you got pass that testing issue.
could just storing it in a persistent variable do the job?
Yeah, you can use the persistence layer to store the access token per user id/device id. The ask-sdk-dynamodb-persistence-adapter
module provides this integration. You can check the high-low game sample on how to use it. Let us know if this helps.
@nikhilym Thank you so much for the confirmation! I will do it now and let you guys know :)
Is anyone else amused by their "helper functions"?
ask_sdk_core.utils.request_util.get_account_linking_access_token(handler_input)
handler_input.request_envelope.context.system.user.access_token
It literally just returns that line. You should already know what's in the request object. It's shorter and more explicit to just get what you need from where it actually exists in most cases vs memorizing longer "helper functions" that have to be imported.
Hey @timothyaaron , thanks for the feedback and sorry for the trouble. We understand the helper functions names are a bit long. Would love to hear thoughts from the community on how we can do better. I know this issue is not related to that. So, to keep this issue clean and for better organization, please go ahead with creating a new issue on the SDK repo so that we can get these helper functions to a better place.
@nikhilym My sb is occupied by CustomSkillBuilder(api_client=DefaultApiClient())
Shall I reassign it to StandardSkillBuilder(table_name="High-Low-Game", auto_create_table=True)
?
As well as, I believe in the repo you mentioned a dynamodb hasn't been manually created from AWS but automatically created on the console itself?
Since I've manually created it, is doing:
table = dynamodb.Table('MyTableName')
AccessToken = handler_input.request_envelope.context.system.user.access_token
response = table.update_item(
Item={
'userId': userId,
'AccessToken': AccessToken
}
)
right? even though as I am not sure how to access the userID :D
Unfortunately, I am quite unfamiliar with the whole flask structure of developing an alexa skill from the repo your linked
Actually, the ones I was referring to don't really need "a better place", in my opinion; they could just be dropped. If there's no additional logic (unlike, say, get_slot_value
)—literally just a one-liner—then it's not really a "helper", it's just an alternative access point (which just adds confusion… again, in my opinion).
I, too, don't want to hijack this issue. Happy to let it go with that.
@danieldhz, looks like @nikhilym's got you taken care of. Good luck.
@danieldhz , reassigning your skill builder should work. You probably should use StandardSkillBuilder(table_name="MyTableName", auto_create_table=False)
, since that is your table name and since you mentioned you already created the table manually in AWS.
As for using the persistence attributes in HandlerInput
through your skill, the default is to use user_id
when adding rows to your table. So don't worry about the partition key unless you want it changed. Here is the interface information in case you want the partition key changed to a different attribute.
If all you need is to save and retrieve the access token through persistence, you can do
# For storing the access token in persistence
persistence_attr = handler_input.attributes_manager.persistent_attributes
persistence_attr['access_token'] = <...whatever value you want to be stored...>
handler_input.attributes_manager.save_persistent_attributes()
# For retrieving the access token
persistence_attr = handler_input.attributes_manager.persistent_attributes
access_token = persistence_attr['access_token']
@timothyaaron , yeah it is a bit confusing to have a helper function which basically is a one-liner. The idea was to have a set of commonly used retrieval utilities as helpers, so that skill devs have a go-to module to use them. It also helps us add validations in future, if needed. Btw, thanks for the feedback :-) and jumping in to help other skill developers. Please stay in the thread and respond when you can :-). Always appreciated !!
Hey @danieldhz , as mentioned in the error , you would need ask-sdk
library to use the StandardSkillBuilder
. You probably have ask-sdk-core
which gives you the basic skill builder functionality (no persistence layer, no api client). Please change your requirements.txt
to use ask-sdk
, install that library and try again.
Hi @danieldhz, based on the conversation I see your issue on 'NoneType' object has been resolved and you have reopened this issue which is already a duplicate from here.
I will close this issue and follow up on the other one in order to keep issue tracker clean and not mix multiples issues.