You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
let target = Target::builder().arn(context.invoked_function_arn).role_arn(context.identity.unwrap().identity_id)// (1)
...
.name(format!("KB2 Vote Close {}", message.id))// (2).schedule_expression(format!("at({})", vote_req.close_at.unwrap()))// (3)
Panic:context.identity is only populated for Cognito-identity invocations; it's None behind API Gateway/function URLs → .unwrap() panics. Even when present, a Cognito identity_id is not an IAM role ARN — EventBridge Scheduler needs an execution role ARN.
Invalid name: Scheduler names must match [0-9a-zA-Z\-_.]{1,64}; spaces cause a ValidationException.
Invalid expression:at() requires yyyy-mm-ddThh:mm:ss; DateTime<Utc>'s Display renders 2026-07-02 12:00:00 UTC.
Callback auth cannot succeed: the scheduled payload sends Authorization: Discord <bot_token> (line 108-113). The Discord branch of auth_middleware builds a Bearer {bot_token} client — Discord rejects a bot token with the Bearer prefix, and even if it authenticated, bots are explicitly rejected (if current_user.bot { 401 }).
Even under the Bot scheme, post_votes_id_close extracts Extension<CurrentUser>, which the Bot branch never inserts → 500.
Because the Discord message is created and the vote saved before the schedule call, the client receives a 500 for a vote that was actually created — the UI then can't tell what happened.
Pass the scheduler execution role via configuration instead of the Lambda context:
.role_arn(std::env::var("SCHEDULER_ROLE_ARN").expect("SCHEDULER_ROLE_ARN must be set"))
Authenticate the callback with the existing Bot scheme (Authorization: Bot <token>), and give the close route a bot-capable path that doesn't require Extension<CurrentUser> — e.g. follow the post_recon pattern (verify/controllers.rs:126-133) of comparing the client token to the bot token, or split an internal close_vote(guild_id, message_id) function that both the admin route and the scheduler target call.
Consider creating the schedule before announcing success, or compensating (log + alert) when scheduling fails after the message is posted (see Vote - Scheduled Closing #6's requirement to alert on failed scheduled calls).
Severity
High — feature cannot work; creating a vote with
close_atset 500s after the Discord message was already postedRelated feature ticket: #6
Location
api/src/guilds/votes/controllers.rs:105-155,api/src/middleware.rs:22-33Problem
Five independent defects in the
close_atpath:context.identityis only populated for Cognito-identity invocations; it'sNonebehind API Gateway/function URLs →.unwrap()panics. Even when present, a Cognitoidentity_idis not an IAM role ARN — EventBridge Scheduler needs an execution role ARN.[0-9a-zA-Z\-_.]{1,64}; spaces cause aValidationException.at()requiresyyyy-mm-ddThh:mm:ss;DateTime<Utc>'sDisplayrenders2026-07-02 12:00:00 UTC.Authorization: Discord <bot_token>(line 108-113). TheDiscordbranch ofauth_middlewarebuilds aBearer {bot_token}client — Discord rejects a bot token with the Bearer prefix, and even if it authenticated, bots are explicitly rejected (if current_user.bot { 401 }).Botscheme,post_votes_id_closeextractsExtension<CurrentUser>, which theBotbranch never inserts → 500.Because the Discord message is created and the vote saved before the schedule call, the client receives a 500 for a vote that was actually created — the UI then can't tell what happened.
Suggested resolution
Botscheme (Authorization: Bot <token>), and give the close route a bot-capable path that doesn't requireExtension<CurrentUser>— e.g. follow thepost_reconpattern (verify/controllers.rs:126-133) of comparing the client token to the bot token, or split an internalclose_vote(guild_id, message_id)function that both the admin route and the scheduler target call.