Closed anilskalyane closed 1 year ago
The following error:
[DEBUG] Error details: TypeError: Cannot read property 'teamId' of undefined, storedResponse: undefined
Comes from one of two locations:
AWSLambdaReceiver
: https://github.com/slackapi/bolt-js/blob/main/src/receivers/AwsLambdaReceiver.ts#L203HTTPModuleFunction
: https://github.com/slackapi/bolt-js/blob/main/src/receivers/HTTPModuleFunctions.ts#L204First question: are you using this app on AWS Lambda, and using it with the AWS Lambda receiver that bolt provides? I believe the answer is "no", as to configure bolt-js for use on AWS Lambda, you must manually instantiate the receiver class and pass it as a receiver
property to your App
constructor. However, what confuses me is you are using the processBeforeResponse
property, which is aimed for use exclusively with the AWS Lambda Receiver. We should clarify this point and ensure you are not setting processBeforeResponse
unnecessarily, as this could also further confuse bolt execution.
Second, I am not exactly sure what is happening in your authorize function, but some observations:
then()
, catch()
) while inside an async
function. This means that the DB connection establishment is done in separate ticks of the JavaScript event loop. This jumps out as me as having the potential for race conditions.Slackbot
construct is or what library this uses, but, if this construct relies on the mongoose DB connection, then it is quite possible, as I mentioned in the previous point, that the mongoose DB connection establishment is not complete by the time Slackbot.find
is called.If my assumption that Slackbot
relies on the mongoose DB connection, then to fix the potential for race conditions, you could change your authorize function to use await
to make sure that the code executes sequentially. Something like the following would address that:
const authorizeFn = async ({ teamId, enterpriseId }) => {
console.log(teamId);
// Fetch team info from database
try {
await db.mongoose
.connect(process.env.DB_CONNECTION_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true
});
console.log("Connected to the database!");
} catch (err) {
console.log("Cannot connect to the database!", err);
process.exit();
}
const data = await Slackbot.find({ "access_details.team.id": teamId });
if (!data)
console.log("Not found slack user with team " + teamId);
else {
console.log(data[0].access_details);
return {
// You could also set userToken instead
botToken: data[0].access_details.access_token,
botId: data[0].access_details.app_id,
botUserId: data[0].access_details.bot_user_id
};
}
}
Thanks for the quick response.
First question ---> Yes, We are not using the Lambda environment. Also removed the processBeforeResponse
param but not luck
Second question: Small info on the Authorization function: Authorization is the process of deciding which Slack credentials (such as a bot token) should be available while processing a specific incoming request.
MongoDB connections and fetching the data part are working as expected but It's failing once auth data is passed to Bolt.
FYI Logs:
0|slack-app | 2023-03-21T19:56:21: TQXMXXXXX
0|slack-app | 2023-03-21T19:56:21: Connected to the database!
0|slack-app | 2023-03-21T19:56:21: {
0|slack-app | 2023-03-21T19:56:21: ok: true,
0|slack-app | 2023-03-21T19:56:21: app_id: 'xxxxx',
0|slack-app | 2023-03-21T19:56:21: authed_user: { id: 'xxxxx' },
0|slack-app | 2023-03-21T19:56:21: scope: 'team:read,app_mentions:read,channels:history,chat:write,commands,im:history,im:read,im:write,users.profile:read,users:read,users:read.email',
0|slack-app | 2023-03-21T19:56:21: token_type: 'bot',
0|slack-app | 2023-03-21T19:56:21: access_token: 'xoxb-xxxxxx-xxxx-xxxxxxx',
0|slack-app | 2023-03-21T19:56:21: bot_user_id: 'xxxxxx',
0|slack-app | 2023-03-21T19:56:21: team: { id: 'xxxxxx', name: 'xxxxxxx' },
0|slack-app | 2023-03-21T19:56:21: enterprise: null,
0|slack-app | 2023-03-21T19:56:21: is_enterprise_install: false
0|slack-app | 2023-03-21T19:56:21: }
0|slack-app | 2023-03-21T19:56:21: [ERROR] An unhandled error occurred while Bolt processed an event
0|slack-app | 2023-03-21T19:56:21: [DEBUG] Error details: TypeError: Cannot read property 'teamId' of undefined, storedResponse: undefined
please let me know if you require any further details
It looks to me like the code now is returning installation data, so progress! But, there is still a Bolt error. Let's take a further look.
This error message:
[ERROR] An unhandled error occurred while Bolt processed an event
... comes, I believe, from this location in the source code. Given that your authorize
function returns a result now (based on your latest logs), I believe this line of Bolt code successfully passes now. However, somewhere else in the enclosing processEvent
method is throwing an exception related to access teamId
on something that is undefined
. So when the HTTP Receiver for your app calls processEvent
here, the catch
clause here is triggered, and that is where the final error logging in your last message is coming from.
The question now is, where in Bolt's processEvent
method is Bolt attempting to access teamId
on an undefined
variable?
To make it easier to parse through and figure that out, could you set developerMode
to true
and socketMode
to false
in your App
constructor and run again and post the logs? The reason I ask for this is to trigger this logging in Bolt so that we can see the details of the event from Slack triggering the exception. This will make it easier to trace through the logic and try to determine why this exception is happening for you.
Also, one more question: what version of bolt-js are you using?
Here are the config and logs,
const app = new App({
appToken: process.env.SLACK_APP_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
logLevel: LogLevel.DEBUG,
customRoutes: customRoutes.customRoutes,
scopes: [process.env.SLACK_SCOPES],
authorize: authorizeFn,
developerMode: true
});
0|slack-app | 2023-03-21T20:52:14: [INFO] An unhandled HTTP request (POST) made to /slack/events was ignored
0|slack-app | 2023-03-21T20:52:15: [INFO] An unhandled HTTP request (POST) made to /slack/events was ignored
0|slack-app | 2023-03-21T20:52:31: [INFO] An unhandled HTTP request (POST) made to /slack/events was ignored
0|slack-app | 2023-03-21T20:53:15: [INFO] An unhandled HTTP request (POST) made to /slack/events was ignored
0|slack-app | 2023-03-21T20:53:45: [INFO] An unhandled HTTP request (POST) made to /slack/events was ignored
Bolt Version - @slack/bolt@3.12.2
@anilskalyane please also set socketMode: false
on your app constructor.
Added the socket mode also, @filmaj
const app = new App({
appToken: process.env.SLACK_APP_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
logLevel: LogLevel.DEBUG,
customRoutes: customRoutes.customRoutes,
scopes: [process.env.SLACK_SCOPES],
authorize: authorizeFn,
developerMode: true,
socketMode: false
});
0|slack-app | 2023-03-21T21:48:31: [DEBUG] bolt-app {"token":"4sND1EQy5IGQaJGjvIguLynn","team_id":"TQXMxxxxx","context_team_id":"TQXMxxxxx","context_enterprise_id":null,"api_app_id":"A04FLDxxxxx","event":{"client_msg_id":"xxxxx-1a99-45e8-a7ef-94ecce2033ae","type":"message","text":"hifives","user":"U01Vxxxxxxx","ts":"1679415510.997069","blocks":[{"type":"rich_text","block_id":"gz3OT","elements":[{"type":"rich_text_section","elements":[{"type":"text","text":"hifives"}]}]}],"team":"TQXMxxxxx","channel":"D04U2xxxxxx","event_ts":"1679415510.997069","channel_type":"im"},"type":"event_callback","event_id":"Ev04VC8Z2K2M","event_time":1679415510,"authorizations":[{"enterprise_id":null,"team_id":"TQXMxxxxx","user_id":"U04U2xxxxxx","is_bot":true,"is_enterprise_install":false}],"is_ext_shared_channel":false,"event_context":"4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUUVhNVlNIUTgiLCJhaWQiOiJBMDRGTEQyVTkwVCIsImNpZCI6IkQwNFUyVFBHWTlaIn0"}
0|slack-app | 2023-03-21T21:48:31: TQXMxxxxx
0|slack-app | 2023-03-21T21:48:31: Connected to the database!
0|slack-app | 2023-03-21T21:48:32: {
0|slack-app | 2023-03-21T21:48:32: ok: true,
0|slack-app | 2023-03-21T21:48:32: app_id: 'A04xxxxx',
0|slack-app | 2023-03-21T21:48:32: authed_user: { id: 'U01V2xxxxx' },
0|slack-app | 2023-03-21T21:48:32: scope: 'team:read,app_mentions:read,channels:history,chat:write,commands,im:history,im:read,im:write,users.profile:read,users:read,users:read.email',
0|slack-app | 2023-03-21T21:48:32: token_type: 'bot',
0|slack-app | 2023-03-21T21:48:32: access_token: 'xoxb-xxxxxxx-xxxxxx-xxxxxxxx',
0|slack-app | 2023-03-21T21:48:32: bot_user_id: 'U04U2xxxxxx',
0|slack-app | 2023-03-21T21:48:32: team: { id: 'TQXMxxxxx', name: 'xxxxxx' },
0|slack-app | 2023-03-21T21:48:32: enterprise: null,
0|slack-app | 2023-03-21T21:48:32: is_enterprise_install: false
0|slack-app | 2023-03-21T21:48:32: }
0|slack-app | 2023-03-21T21:48:32: [ERROR] An unhandled error occurred while Bolt processed an event
0|slack-app | 2023-03-21T21:48:32: [DEBUG] Error details: TypeError: Cannot read property 'teamId' of undefined, storedResponse: undefined
Sorry for the delay, I have not forgotten about this, just caught up in some other work related stuff! As soon as I have some time to dig deeper in here, I will do so.
@filmaj - Thanks for looking into this! I'll be happy to provide more details and try some other things if it helps
So I tried to just trace through the code to see if I could glean where things are breaking.. but bolt is of such complexity that once it became time to trace through the middleware-processing code Bolt has, I gave up.
I think a better angle is to try to reproduce the issue. To that end, how can I reproduce this? It sounds like you are "adding this app to another workspace (different from where the app was created)." How did you add this app to the new workspace? Typically this is done using an OAuth flow. Is that the case here? How are you managing the OAuth dance? Looks like your app is adding 'custom routes' - is it through those custom routes that you are managing the installation route (OAuth entry point) as well as the OAuth authorization route?
If so, this is a out-of-the-box feature that Bolt provides (to manage these OAuth endpoints) that we call Installation Store - is there a reason you are not using that feature?
@filmaj thanks for your time and patience.
Yes, I'm trying to install the app to a different workspace using OAuth flow. here is the complete code for your reference,
const { App, LogLevel } = require('@slack/bolt');
const HifivesApi = require('./src/controllers/HifivesApi.js');
const db = require("./src/models");
const Slackbot = db.slackbot;
const app_menu = require('./src/config/menu.json')
const databaseData = {};
const database = {
set: async (key, data) => {
databaseData[key] = data
},
get: async (key) => {
return databaseData[key];
},
};
const authorizeFn = async ({ teamId, enterpriseId }) => {
console.log(teamId);
// Fetch team info from database
await db.mongoose
.connect(process.env.DB_CONNECTION_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => {
console.log("Connected to the database!");
})
.catch(err => {
console.log("Cannot connect to the database!", err);
process.exit();
});
await Slackbot.find({ "access_details.team.id": teamId })
.then(data => {
if (!data)
console.log("Not found slack user with team " + teamId);
else {
console.log(data[0].access_details);
return {
// You could also set userToken instead
botToken: data[0].access_details.access_token,
botId: data[0].access_details.app_id,
botUserId: data[0].access_details.bot_user_id
};
}
})
.catch(err => {
console.log("Error retrieving slack with team=" + teamId);
});
}
// Initializes your app with your bot token and app token
const app = new App({
socketMode: false,
appToken: process.env.SLACK_APP_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
logLevel: LogLevel.DEBUG,
stateSecret: 'my-state-secret',
scopes: [process.env.SLACK_SCOPES],
authorize: authorizeFn,
developerMode: true,
installationStore: {
storeInstallation: async (installation) => {
if (installation.isEnterpriseInstall && installation.enterprise !== undefined) {
return await database.set(installation.enterprise.id, installation);
}
if (installation.team !== undefined) {
return await database.set(installation.team.id, installation);
}
throw new Error('Failed saving installation data to installationStore');
},
fetchInstallation: async (installQuery) => {
if (installQuery.isEnterpriseInstall && installQuery.enterpriseId !== undefined) {
return await database.get(installQuery.enterpriseId);
}
if (installQuery.teamId !== undefined) {
return await database.get(installQuery.teamId);
}
throw new Error('Failed fetching installation');
},
deleteInstallation: async (installQuery) => {
if (installQuery.isEnterpriseInstall && installQuery.enterpriseId !== undefined) {
return await database.delete(installQuery.enterpriseId);
}
if (installQuery.teamId !== undefined) {
return await database.delete(installQuery.teamId);
}
throw new Error('Failed to delete installation');
},
},
installerOptions: {
directInstall: true,
}
});
app.error(async (error) => {
// Check the details of the error to handle cases where you should retry sending a message or stop the app
console.error(error);
});
app.message('hifives', async ({ message, client, logger, ack }) => {
console.log("message data", message);
try {
ack();
await client.chat.postEphemeral({
channel: message.channel,
user: message.user,
blocks: [{
"type": "section",
"text": {
"type": "mrkdwn",
"text": `Welcome <@${message.user}>! \n\nWhat would like to do today?`
}
},
{
"type": "divider"
},
app_menu
]
});
}
catch (error) {
logger.error(error);
}
});
async function userBasicDetails(userId) {
try {
// Call the users.info method using the WebClient
const result = await app.client.users.info({
user: userId
});
//console.log(result.user.profile);
return result.user.profile;
}
catch (error) {
console.error(error);
}
}
async function postStatusMessage(client, channelId, userId, msg, prefix = "ERROR :") {
const msgText = prefix ? prefix + msg : msg;
if (channelId) {
await client.chat.postEphemeral({
channel: channelId,
user: userId,
text: msgText
})
} else {
await client.chat.postMessage({
channel: userId,
text: msgText
});
}
return true;
}
/** Start Bolt App */
(async () => {
try {
await app.start(process.env.PORT || 3000);
console.log('⚡️ Bolt app is running! ⚡️');
} catch (error) {
console.error('Unable to start App', error);
}
})();
@anilskalyane I am glad I asked the question as I see two problems:
authorize
and installationStore
options. installationStore
aims to handle the OAuth ritual, but also, via the fetchInstallation
function, does the exact work that the authorize
function would do. My recommendation to you would be to only use the installationStore
and drop the authorize
function. We call this out in the authorize
function documentation: "If you’re using the built-in OAuth support authorization is handled by default, so you do not need to pass in an authorize
option." (the "built-in OAuth support" here being the installationStore
installationStore
uses one storage method for storing installation and token information (in-memory via the databaseData
object and database
interface) while your authorize
function interacts with a Mongo database, those two are at odds. Choose one storage system for your app's installation information (recommended to use a database that can persist data across restarts, like Mongo, over an in-memory solution like the databaseData
object your code employs)If I am missing some aspect of your requirements where you believe you need to leverage both authorize
and installationStore
, please let me know. If the documentation was unclear on this topic, that is also helpful to know!
I also noticed that in your latest code, you no longer define any customRoutes
. Let's try to create a reproduction case with a single set of application options. Changing the options around makes this a more difficult task.
It worked 🤯 and thank you for the quick response and help. 🙌
👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.
As this issue has been inactive for more than one month, we will be closing it. Thank you to all the participants! If you would like to raise a related issue, please create a new issue which includes your specific details and references this issue number.
We are facing the following issue with the Slack app when we are trying to add the app to another workspace (different from the one where it was created). Once the authorization is given, we are able to read all the credentials including team id, access token, etc. However, when we try to use the same credentials to access the workspace for any activity,
we are getting the following error:
I think getting this response from Slack bolt,
In the first line, I'm printing the teamId and same also passing it to the bolt lib(authorizer),
Code: