Skip to content

Troubleshooting

Sebastian F. Markdanner [MVP] edited this page May 24, 2026 · 4 revisions

Common issues for the first public release. If your problem isn't here, check the FAQ, search open issues, or file a bug report.

Two flavours, one codebase. The managed portal at https://portal.pimactivation.com and any self-hosted instance run identical code. The differences are which app registration backs them and how it was consented. Sections below call out which scenario applies when it matters; everything unmarked applies to both.

First aid

When something looks wrong, try this in order before reading further:

  1. Click Refresh in the header. This invalidates the role and policy caches and re-fetches everything from Microsoft Graph and ARM.
  2. Open the activity drawer (bell icon) and look for failed operations — toasts are summarised there with the underlying error from Graph/ARM.
  3. Hard reload (Ctrl/Cmd+Shift+R). Tokens live in sessionStorage and survive a normal reload, but a hard reload also clears stale module state.
  4. Sign out and back in. From the user menu, choose Sign out, then sign in again — picking the right account if you have several.
  5. Open DevTools → Console and Network. Most "weird" symptoms have a clear MSAL error code (AADSTSxxxxx, interaction_in_progress) or an HTTP response with a claims challenge that pinpoints the cause.

Sign-in and authentication

Blank app shell after sign-in

The HTML shell loaded but the app failed to render. The <div id="app"> stays hidden until MSAL initialises successfully and the first roles fetch returns. Most common causes:

  • Self-hosted with placeholders left in Portal/js/msal-config.js. The deployment script's "verifying injection" step should fail loudly if __PORTAL_CLIENT_ID__ or __PORTAL_TENANT_ID__ weren't replaced. Re-run the deployment with the same parameters — each run uses a fresh deploymentScriptRunId, so retries are safe.
  • Wrong tenant or wrong account. You signed in with an account whose home tenant doesn't have the app registration (typical for self-hosted), or the managed portal can't get a token for the directory you picked. Sign out, then sign in again and choose the correct account in the Microsoft account picker.
  • Browser blocking login.microsoftonline.com. Strict third-party-cookie modes, tracking-protection extensions, or enterprise web filters can break the redirect handshake. Try the portal in a clean profile or a private window with extensions disabled to confirm.
  • CSP blocked a critical script. The portal sets a strict Content-Security-Policy in staticwebapp.config.json. If you've forked and added a script from a domain that isn't allowlisted, you'll see the violation in DevTools → Console.

AADSTS500113: No reply address is registered for the application

The redirect URI on the app registration doesn't match the URL you're loading the portal from.

  • Managed portal: This should never appear on portal.pimactivation.com. If it does on a different host that loads the same code, you're pointing a fork at the managed app's client ID — use your own app registration instead.
  • Self-hosted: Add the SWA URL (and any custom domain) under Authentication → Single-page application in the app registration. The Bicep deployment outputs the exact URIs in redirectUris; the script tries to apply them automatically, but falls back to a warning if the deploying identity lacks the Graph permission to update the app.

AADSTS65001: The user or administrator has not consented to use the application

Your tenant requires admin consent before users can sign in.

  • Managed portal: A Global Admin (or Privileged Role Admin) in your tenant must grant consent once. The tenant in the URL is the consenting tenant — i.e. your tenant, not the publisher's. Either of these works:
  • Self-hosted: Use the adminConsentUrl output from the Bicep deployment, or visit the same URL pattern with your own client ID. The tenant segment is still the consenting tenant.

After admin consent, individual users in that tenant do not need to consent again.

AADSTS50020, AADSTS500011, or "user not found in tenant"

These errors come from the tenant switcher, not initial sign-in. The managed portal authenticates against the organizations authority, so the initial sign-in always lands in your account's home tenant — there is no Microsoft tenant picker before you reach the app.

After sign-in, the Switch tenant button lists every directory your account can reach via ARM (home, guests, member). Picking one triggers a fresh token request against that tenant; that's where these errors surface, and they typically mean one of:

  • AADSTS50020 — your account isn't actually a member or guest of the target tenant (ARM listed it for some related reason but Entra rejects the token request).
  • AADSTS500011 — the portal's app registration isn't provisioned in the target tenant. For the managed portal, that means an admin in that tenant hasn't consented yet — see the admin consent URL above.
  • "user not found in tenant" — same root cause as AADSTS50020.

Click Back / switch to a tenant where you do have eligibilities, or have an admin in the failing tenant grant consent and try again.

"interaction_in_progress" errors in the console

A previous interactive flow didn't clean up — usually because a redirect was interrupted (browser closed mid-flow, MFA prompt cancelled, network blip). Close the tab, reopen the portal, and sign in again. If it persists, open DevTools → Application → Storage and clear sessionStorage for the portal origin.

Tenant switcher is empty or only shows the home tenant

Switch tenant calls ARM's tenants endpoint to enumerate every directory your account can reach (home, guests, member). Possible causes for a short list:

  • Your account genuinely has access to only one directory.
  • A Conditional Access policy on a guest tenant blocks ARM token acquisition.
  • ARM consent for the managed app hasn't completed in the target tenant. Click Grant access in the banner to retry the ARM consent leg.

The chosen tenant is stored in sessionStorage under pim-portal-preferred-tenant and survives a refresh of the same tab — but not closing the tab.

"Azure roles could not be loaded — additional permissions are required"

The ARM consent leg failed. The portal will still load Entra and Group roles. Click Grant access in the banner to trigger an interactive ARM consent. If your tenant requires admin consent, the consent screen will say so — escalate to an admin.

Roles and policy

No eligible roles appear

  • You may genuinely have no eligibilities in the current tenant. Switch tenants from the header.
  • Check the type filter pills at the top of each table (User / Group / Azure / All). A plane may be filtered out from a previous session — settings persist in localStorage.
  • Some directories restrict Policy.Read.All or RoleManagement.ReadWrite.Directory such that the eligibility queries return empty. Ask an admin to grant tenant-wide admin consent for the portal app.
  • For Azure Resource roles, your account must be able to enumerate role assignments at the tenant root via ARM's asTarget() filter. Most directories allow this for any signed-in user, but custom RBAC can block it.

A role I expect is missing

  • Check the Active Roles tab — it might already be active. The eligible table hides already-active roles by default.
  • Toggle Show already-active roles in eligible in Settings to confirm.
  • Check the type filter pills; a plane may be filtered out.
  • For AU-scoped roles, expand the row — the AU display name is resolved on demand and the row title shows only the role name.

Policy matrix is missing or shows "—"

The 30-minute in-memory policy cache may be stale, or a per-role policy fetch failed silently and the role rendered without details.

  • Click Refresh in the header to invalidate both caches and re-fetch.
  • Check DevTools → Console for warnings of the form "policy fetch failed for …". These are non-fatal but indicate a Graph permission, throttling, or network issue.
  • AU-scoped Entra roles use a tenant-root bulk policy fetch to avoid 403s; if that bulk fetch is denied, every Entra role will render without policy details.

Policy says "Auth context required" but I don't see a prompt

The portal detects auth-context requirements from policy and pre-emptively triggers a step-up before activation. The step-up is a full-page redirect to login.microsoftonline.com and back — not a popup. If the redirect never happens, check that browser extensions or enterprise web filters aren't blocking navigation to login.microsoftonline.com, then click Activate again.

Activation

403 when activating

Almost always one of:

  • The role policy requires an auth context that your token doesn't satisfy. Open the role policy in Entra and check Activation requirements. The portal should detect this and step you up automatically; if it doesn't, please file a bug with the role definition ID and the policy.
  • The role requires approval and the request was rejected by an approver — see the activity drawer.
  • A Conditional Access policy unrelated to PIM (e.g. requiring a compliant device, sign-in frequency, or named location) is blocking the activation request itself. The Network tab will show the underlying claims challenge or a denyReason from CA.
  • Your account temporarily lost the eligibility between page load and activation (rare but possible). Refresh and try again.

401 with insufficient_claims in the network tab

This is normal — every Graph and ARM client in the portal recognises the WWW-Authenticate: insufficient_claims header (or the equivalent JSON body), pauses the operation, calls MSAL with the requested claims, and resumes with the new token. You'll see a redirect to login.microsoftonline.com and back, then the original operation completes.

If it loops or fails outright, file a bug with the offending request URL, the decoded claims challenge, and a HAR if you can.

Activation succeeds but role doesn't appear in Active Roles

  • The role likely requires approval and is in PendingApproval state — it appears in the Active table with a "Pending approval" tag and in the activity drawer.
  • For Azure Resource roles, ARM's view of the assignment can lag a few seconds behind a successful response. Click Refresh.

Pending approval never resolves on its own

The portal does not currently poll for approval state. Refresh manually after your approver acts. (Tracked on the Roadmap.)

Bulk activation: some roles succeeded, some failed

Open the activity drawer and click the operation to see the per-role outcome. Each row shows the role name, plane, status (Activated, PendingApproval, Failed), HTTP status, and the underlying error string. Common per-role failures:

  • Justification too short — most policies require ≥ 10 characters; the server returns a 400 with a specific message.
  • Ticket number required — surface the field by re-opening the activation dialog; it's only shown when policy requires it.
  • Duration exceeds policy maximum — reduce the requested duration.
  • MFA / strong auth required — Microsoft returns a claims challenge; the portal will step you up and the next attempt should succeed.

"Ticket" field doesn't appear

The Justification field is always shown — whether the policy lists it as required or optional. Other fields (ticket number, ticket system, custom duration) only render when at least one role being activated has a policy that requires them. If you expected the ticket field for a role and it's missing, the policy may have changed since the cache was populated — click Refresh to invalidate the cached policy and reopen the activation dialog.

Activation profiles

A saved profile is gone after switching browsers or devices

Activation profiles are stored in your browser's IndexedDB (database pimactivation-profiles). They don't sync automatically — each browser profile, device, or private window starts with its own empty list. To move them, use the Export and Import buttons in the Profiles modal: Export writes a JSON file (pim-activation-profiles-YYYY-MM-DD-HHMM.json) you can re-import on another browser or machine.

A profile is missing in one tenant but present in another

In Settings, the Tenant-scoped activation profiles option is on by default. With it on, profiles are visible only in the tenant they were created in (legacy profiles from before this option are migrated and remain visible everywhere). Turn the option off to share profiles across all tenants.

"Profile failed to save" or profiles never persist

  • Private / incognito browsing disables IndexedDB writes in some browsers.
  • Site storage quota exceeded — clear stale data in DevTools → Application → Storage.
  • Some enterprise browser policies disable IndexedDB entirely. Try a different browser to confirm.

A role in a profile no longer activates

The role definition or eligibility was removed in Entra. The portal still sends the request and surfaces the error from the server. Edit the profile to remove the stale role.

Imported profiles show roles as unavailable

Profile role IDs are tenant-specific. Importing a profile exported from a different tenant keeps the profile but its roles won't match anything in your current eligible list — you'll see a "… role(s) are no longer eligible and were skipped" warning when you try to activate. Either switch to the originating tenant or edit the profile to map it to roles in the current tenant.

"Import failed: Invalid JSON file or format"

The file you picked isn't a profiles export. Import only accepts JSON files produced by Export — a top-level array of profile objects with the expected fields. If you've hand-edited the file, check that it still parses as JSON and that each profile has at least name and roles.

UI, settings, and appearance

Theme doesn't follow OS preference

Once you pick Light, Dark, or High contrast explicitly in Settings, that overrides the OS preference. Choose System to restore auto-detect. The choice is persisted in localStorage under pim-portal-theme.

Favicon doesn't change after a sync

Browser favicon caches are sticky. Open in a private window or fully clear site data for the portal origin to validate.

Filters or section layout reset between sessions

Persistence options live in Settings (e.g. Persist quick filters, Persist section state, Swap eligible/active sections). They store to localStorage; if you've cleared site data or your browser quota was reclaimed, they'll reset.

Tokens disappear on tab close

By design. Tokens live in sessionStorage, which survives reload but not tab close. After the tab closes, MSAL silent acquisition needs a fresh interactive sign-in.

Activity drawer is empty after a refresh

The activity drawer is also session-scoped — it's cleared on tab close. Operations are not persisted server-side because the portal has no server.

Performance and throttling

Roles take a while to appear after sign-in

Entra and Group calls render first; Azure Resource calls usually arrive shortly after. In tenants with many subscriptions and many resource roles, ARM enumeration can take several seconds even with the asTarget() tenant-root path. Subsequent visits in the same tab benefit from the per-tenant role cache in localStorage.

Many 429 responses in DevTools during activation

The Graph batch engine handles 429 by honouring Retry-After and applying exponential backoff. Brief bursts are normal during bulk activations of 20+ roles. If they persist or the operation fails, your tenant may be applying aggressive throttling — file a bug with the captured 429 response headers.

ARM 429 or 5xx during Azure activation

ARM activations run with bounded concurrency, but ARM's per-tenant write limits are stricter than Graph's. If a bulk Azure activation partially fails with 429 or 503, retry just the failed rows from the activity drawer.

Portal feels slow after several tenant switches

Each tenant switch triggers a redirect and refetches everything for the new tenant. The role and policy caches are per-tenant, so switching back to a recent tenant is fast; switching to a brand-new one will take a full load.

Self-hosted deployment

These items only apply to self-hosted instances. The managed portal is built and deployed by CI; you should never see them on portal.pimactivation.com.

ERROR: portalSourceArchiveUrl downloaded successfully, but the ZIP does not contain Portal/index.html

Your supplied archive doesn't contain a Portal/ folder at the expected depth. The script searches up to four directory levels from the ZIP root for Portal/index.html. Verify the archive structure and re-run.

Deployment script errors fetching from a private branch

GitHub returns 404 to unauthenticated downloads of private repositories. Either:

  • Deploy from a public branch with portalSourceBranch, or
  • Build a ZIP locally and host it on a publicly reachable URL (e.g. a Blob with a short-lived SAS) and pass it as portalSourceArchiveUrl.

Deployment finishes with a Microsoft Graph permission warning

The deployment identity could not update the app registration's redirect URIs via Graph. Add the values from the Bicep redirectUris output manually under Authentication → Single-page application in the app registration.

Deployment fails after a previous failed run with ResourceFileShare ... Sharing violation

The fix is already built in — each run uses a fresh resource name driven by deploymentScriptRunId. Just rerun the template; the new run gets a new resource name automatically.

Custom domain shows a redirect mismatch after binding

After binding the domain on the SWA, make sure the same domain is listed under your app registration's SPA redirect URIs. The deployment script attempts to add it; if the attempt failed, add it manually.

Self-hosted portal loads but signs in to the wrong tenant

For a single-tenant self-hosted deployment the Bicep template injects your tenant ID directly into Portal/js/msal-config.js (replacing __PORTAL_TENANT_ID__). If the deployed file still contains organizations or a placeholder, the injection step failed — re-run the deployment.

CSP violations after customising the portal

The default staticwebapp.config.json enforces a strict CSP that allows scripts only from self and cdn.jsdelivr.net, and connects only to login.microsoftonline.com, graph.microsoft.com, and management.azure.com. If you've added telemetry, custom CDNs, or third-party scripts, extend the CSP accordingly. See Security and Privacy for the full header set.

Filing a good bug report

When opening an issue, include:

  • Whether you're on portal.pimactivation.com or a self-hosted build (and the build SHA if known).
  • Browser and version, OS, and whether the issue reproduces in a private window with extensions disabled.
  • The MSAL error code or HTTP status from DevTools, plus the request URL.
  • The decoded claims challenge if the failure is auth-related.
  • A short HAR (with sensitive headers redacted) for activation failures.
  • Whether the role is Entra, Group, or Azure Resource, and whether it's AU-scoped.

The more of these you include, the faster the fix.

Clone this wiki locally