Budibase/budibase · error · HTTPError
Link token is invalid or has expired
Error message
Link token is invalid or has expired
What it means
handoffChatLinkSession looks up the stored chat identity link session by the token in the URL. If sdk.ai.chatIdentityLinks.getChatIdentityLinkSession returns nothing, the token does not exist, was already consumed, or has been cleaned up — so the flow cannot continue and a 400 is thrown.
Source
Thrown at packages/server/src/api/controllers/ai/chatIdentityLinks.ts:213
if (window.opener && !window.opener.closed) {
try {
window.opener.focus()
window.close()
} catch (error) {}
}
</script>
</body>
</html>`
}
export async function handoffChatLinkSession(
ctx: UserCtx<void, string, { instance: string; token: string }>
) {
const token = resolveToken(ctx.params.token)
const session =
await sdk.ai.chatIdentityLinks.getChatIdentityLinkSession(token)
if (!session) {
throw new HTTPError("Link token is invalid or has expired", 400)
}
assertSessionMatchesInstance({
workspaceId: session.workspaceId,
instance: ctx.params.instance,
})
if (!ctx.isAuthenticated) {
utils.setCookie(
ctx,
`/api/chat-links/${ctx.params.instance}/${token}/handoff`,
CHAT_LINK_RETURN_URL_COOKIE,
{ sign: false }
)
ctx.redirect(BUILDER_LOGIN_PATH)
return
}
const currentGlobalUserId = getCurrentGlobalUserId(ctx)View on GitHub (pinned to a81a902e9a)
Solutions
- Request a fresh identity link from the chat channel and use the new token
- Verify the token string in the URL is complete and unmodified
- Check the backing store for the link session document (it may have been consumed or TTL-expired)
- If tokens expire too fast, adjust the session TTL/retention in chatIdentityLinks configuration
Example fix
// before const session = await getChatIdentityLinkSession(oldToken) // already consumed // after const newToken = await createChatIdentityLink(...) const session = await getChatIdentityLinkSession(newToken)
Defensive patterns
Strategy: try-catch
Try / catch
try {
await api.handoffLink(instance, token)
} catch (e) {
if (e instanceof HTTPError && e.status === 400 && e.message.includes("invalid or has expired")) {
// issue a fresh link token in chat and retry once with the new token
}
throw e
} Prevention
- Treat link tokens as single-use: open handoff links exactly once
- Use links promptly before expiry
- Persist tokens in durable storage in production, not memory
- Copy links verbatim from the chat message
When it happens
Trigger: GET/POST to /api/chat-links/:instance/:token/handoff with a token that was never issued, already used (one-time handoff), or expired/deleted in the data store.
Common situations: Clicking a handoff link a second time after it was already completed; links left unused past their expiry/TTL; restarting with an in-memory or dev database that lost the session doc; typo'd or truncated token copied from chat.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- token is required
- Invalid bookmark query
- Invalid limit query
- Limit query must be between 1 and 100
- Invalid ${queryName} query
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/9d6451fdd006d030.
Report an issue: GitHub.