-
Notifications
You must be signed in to change notification settings - Fork 421
Description
Bug Report: static_select initial_option Not Persisting After chat.update on Tab Switch
Summary
When updating a message containing a static_select element via chat.update API with a new initial_option, the selection appears correct immediately after the update. However, when users switch tabs or conversations and return, the dropdown reverts to the previous selection, even though the server-side message state is correct (verified by full page refresh).
Environment
- Slack API: Web API
- Method:
chat.update - Block Element:
static_select - Client: Slack Desktop App (macOS), Slack Web
- Slack API Package:
@slack/web-apiv7.9.2 - Node.js: v18+
Steps to Reproduce
- Send initial message with a
static_selectdropdown and action buttons:
const blocks = [
{
type: 'context',
block_id: 'assignment_status',
elements: [{
type: 'mrkdwn',
text: '👤 *Status:* Unassigned'
}]
},
{
type: 'actions',
block_id: 'assignment_actions',
elements: [
{
type: 'button',
action_id: 'assign_to_me',
text: { type: 'plain_text', text: '🙋 Assign to me' },
style: 'primary'
},
{
type: 'static_select',
action_id: 'assign_user',
placeholder: { type: 'plain_text', text: 'Assign to team member' },
options: [
{ text: { type: 'plain_text', text: 'User A' }, value: 'user_a_id' },
{ text: { type: 'plain_text', text: 'User B' }, value: 'user_b_id' },
{ text: { type: 'plain_text', text: 'User C' }, value: 'user_c_id' }
]
}
]
}
]-
User selects "User A" from dropdown
- Backend receives
block_actionspayload - Backend calls
chat.updatewithinitial_optionset to User A - ✅ Dropdown correctly shows "User A" selected
- Backend receives
-
Switch to another Slack channel, then switch back
- ✅ Dropdown still shows "User A" (correct)
-
User clicks "Assign to me" button (to assign to User B)
- Backend receives
block_actionspayload - Backend rebuilds
static_selectwith fresh options array - Backend explicitly sets
initial_optionto User B (with matching option from options array) - Backend calls
chat.updatewith complete blocks - ✅ Dropdown immediately shows "User B" selected
- Backend receives
-
Switch to another Slack channel, then switch back
- ❌ Dropdown shows "User A" (wrong!)
- ✅ Status text shows "User B" (correct - from context block)
-
Refresh the entire Slack page/app
- ✅ Dropdown now shows "User B" (correct)
Expected Behavior
After chat.update is called with a new initial_option, the static_select dropdown should display the newly selected option consistently across:
- Immediate rendering after update ✅
- Tab switches ❌ (BUG)
- Page refreshes ✅
Actual Behavior
The static_select dropdown reverts to a previously cached selection after tab switches, despite:
- The
chat.updateAPI call succeeding - Other blocks in the same message updating correctly
- Full page refresh showing the correct selection
- The server-side message state being correct (verified via
conversations.historyAPI)
Code Example
Backend Action Handler
async function handleSlackAction({ payload }) {
const channel = payload.channel.id
const ts = payload.message.ts
const messageBlocks = payload.message.blocks
// User clicked "Assign to me" button
if (payload.actions[0].action_id === 'assign_to_me') {
// Fetch fresh list of users
const users = await fetchAllUsers() // Returns: [{ id: 'user_a_id', name: 'User A' }, ...]
// Find the assignment blocks
const statusIndex = messageBlocks.findIndex(b => b.block_id === 'assignment_status')
const actionsIndex = messageBlocks.findIndex(b => b.block_id === 'assignment_actions')
// Update status block
messageBlocks[statusIndex] = {
type: 'context',
block_id: 'assignment_status',
elements: [{
type: 'mrkdwn',
text: '👤 *Status:* User B' // New assignee
}]
}
// Rebuild static_select with explicit properties (no spreading)
messageBlocks[actionsIndex] = {
type: 'actions',
block_id: 'assignment_actions',
elements: [
{
type: 'button',
action_id: 'assign_to_me',
text: { type: 'plain_text', text: '🙋 Assign to me' },
style: 'primary'
},
{
type: 'static_select',
action_id: 'assign_user',
placeholder: { type: 'plain_text', text: 'Assign to team member' },
options: users.map(u => ({
text: { type: 'plain_text', text: u.name },
value: u.id
})),
initial_option: { // Setting to User B
text: { type: 'plain_text', text: 'User B' },
value: 'user_b_id'
}
}
]
}
// Update message
await slack.chat.update({
channel,
ts,
blocks: messageBlocks,
text: 'Meeting assigned'
})
// Return empty response (tried returning blocks too - same issue)
return new Response('', { status: 200 })
}
}What We've Tried
-
Explicitly reconstructing the element (no object spreading):
return { type: 'static_select', action_id: el.action_id, placeholder: el.placeholder, options: el.options, initial_option: matchingOption // Taken from options array }
-
Re-fetching options from database (not from payload):
- Ensures fresh options array with no stale references
-
Deep cloning all blocks:
const nextBlocks = JSON.parse(JSON.stringify(blocks))
-
Returning blocks in response vs not returning blocks:
- Both exhibit the same issue
-
Using
initial_optionthat matches options array:const matchingOption = el.options.find(opt => opt.value === newUserId) return { ...el, initial_option: matchingOption }
Additional Observations
-
Other block types update correctly: The
contextblock (status text) always shows the correct value after tab switch -
Full refresh works: Suggests server state is correct, but client cache is stale
-
Issue is specific to tab switching: Direct interaction after update shows correct state
-
Happens on both Desktop and Web clients: Not platform-specific
-
Only affects
static_selectafter button interactions: Dropdown-to-dropdown selections persist correctly across tab switches
Hypothesis
Slack's client appears to cache static_select state independently from the message blocks returned by the server. When a tab switch occurs:
- Client checks its cache for the message
- For
static_selectelements, it may be using a cached selection state that wasn't properly invalidated - Other block types re-render from server state correctly
- Full page refresh clears all caches and fetches fresh from server
Impact
- Severity: High - Causes user confusion when UI shows incorrect state
- Frequency: Reproducible 100% of the time with the specific interaction pattern
- User Experience: Users see wrong assignments after tab switches, requiring page refresh
Expected Fix
The static_select element should respect the initial_option value from the server-side message state (as returned by chat.update) when re-rendering after tab switches, consistent with how other block elements behave.