Microsoft Teams Error: 'FailedToOpenWindow' — Cause, Fix, and Troubleshooting Guide
Fix Teams JS SDK 'FailedToOpenWindow' from authentication.authenticate(): call from a user gesture, allow the domain in validDomains, handle mobile.
- #microsoft-teams
- #troubleshooting
- #errors
- #teams-js
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
Your Teams tab starts an authentication popup with the Teams JavaScript SDK, and instead of a login window the authentication.authenticate() call rejects with an SdkError whose code is:
FailedToOpenWindow
The SDK could not open the auth window. This is a host/browser behavior problem, not a bad password or a token error. The most common triggers are calling authenticate() outside a direct user gesture (so the host or browser blocks the popup), running on Teams mobile where the window opens through a different mechanism, or pointing the flow at a start URL whose domain is not listed in your app manifest’s validDomains. Because it is an SdkError, you get a structured { errorCode, message } object you can branch on rather than a bare string.
Symptoms
authentication.authenticate()rejects with anSdkErrorwhereerrorCodemaps toFailedToOpenWindow.- The failure is immediate — no login window ever appears.
- It reproduces reliably on Teams mobile but is intermittent on desktop/web.
- Triggering auth from a timer, promise chain, or
useEffectfails, while a button click sometimes works. - The auth-start page loads standalone in a browser but never opens inside Teams.
- Popup-blocker indicators appear in the browser when the flow is initiated without a click.
Common Root Causes
- No user gesture. Browsers and the Teams host only allow popups synchronously from a real user interaction; calling
authenticate()from an async callback or on load is blocked. - Popup blocked. A browser or host popup blocker prevents the window from opening at all.
- Mobile window semantics. Teams mobile opens the auth window differently; code that assumes a desktop popup can hit
FailedToOpenWindow. - Start URL domain not in validDomains. The
urlpassed toauthenticate()lives on a domain the manifest does not allow, so the host refuses to open it. - Deferred call after
await. Awaiting other work beforeauthenticate()breaks the synchronous link to the user gesture that the popup requires. - Multiple concurrent auth attempts. Firing
authenticate()twice can leave the second call unable to open a window.
How to diagnose
Log the full SdkError so you can read the errorCode and message instead of guessing:
import { authentication } from "@microsoft/teams-js";
document.getElementById("login").addEventListener("click", async () => {
try {
const result = await authentication.authenticate({
url: `${window.location.origin}/auth-start`,
width: 600,
height: 535,
});
console.log("auth success", result);
} catch (err) {
// err is an SdkError: { errorCode, message }
console.error("authenticate failed:", err.errorCode, err.message);
}
});
If err.errorCode corresponds to FailedToOpenWindow, check three things in order: was authenticate() reached synchronously from the click, is the url origin listed in the manifest, and does it reproduce on mobile only.
Confirm the start domain is allowed. Print the origin you are handing to authenticate() and compare it against validDomains in your manifest.json:
console.log("auth start origin:", new URL(`${window.location.origin}/auth-start`).host);
If that host is not in validDomains, the host will refuse to open the window regardless of the gesture.
Fixes
Call authenticate() directly inside a click handler so the popup is tied to a user gesture. Do not await unrelated work first, and do not launch it from useEffect, a timer, or a resolved promise:
loginButton.onclick = () => {
authentication
.authenticate({
url: `${window.location.origin}/auth-start`,
width: 600,
height: 535,
})
.then((result) => onAuthSuccess(result))
.catch((err) => {
if (String(err.errorCode) && err.message) {
showRetryButton(); // let the user re-trigger from a fresh click
}
});
};
Add the auth-start page’s domain to validDomains in your manifest so the host is willing to open it:
{
"validDomains": ["yourapp.example.com"]
}
Handle mobile explicitly. On Teams mobile the window opens through the host rather than as a browser popup; make sure your start and callback pages call app.initialize() and use authentication.notifySuccess() / authentication.notifyFailure() to close the flow, and provide a visible retry affordance since a single blocked attempt is more likely there.
Finally, always give the user a way to retry from a new click. Because the window must open from a gesture, an automatic retry will just fail again — surface a button instead.
What to watch out for
- Never auto-start auth on load. Popups opened without a click are blocked; always require a user action.
- Keep the call synchronous to the gesture. Any
awaitbeforeauthenticate()can sever the user-gesture link the popup needs. - Test on mobile, not just desktop.
FailedToOpenWindowfrequently shows up only on Teams mobile. - List every auth domain in validDomains. The start page and any redirect domains must all be allowed, or the window will not open.
- Provide a retry button. Silent retries cannot succeed for a gesture-gated popup; let the user re-trigger.
- Do not fire concurrent auth calls. One in-flight
authenticate()at a time; guard against double clicks.
Related
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.