Repository navigation
Web Tracking Troubleshooting
Troubleshooting → Web Tracking Troubleshooting
This page is for administrators who use SharePoint web tracking. The AITracker script, installed on your SharePoint sites, records page views, clicks and searches in Application Insights, and the AppInsightsImporter WebJob copies them into SQL. This page covers web-traffic data that is missing or partial, page properties that don't arrive, and the errors the installer can hit when it signs in to SharePoint to deploy the tracker. It describes Stable 1856 and later. On an older build, check Known issues → Fixed issues first: upgrading may be the fix.
If the tracker script itself doesn't load on a page, see Enable CSP for AITracker and web tracking setup.
On this page:
- A day of web traffic failed to save
- Application Insights returned partial results
- Clicks, or a second person's page views, are missing
- A web-traffic import cycle fails
- Repairing page load times stored by older builds
- SharePoint install sign-in errors
- Unexpected SharePoint page properties
How the import keeps its place. The importer reads what Application Insights has ingested since its last cycle, whatever day the page views are dated, and keeps its place in an ingestion-time cursor, in the AppInsightsImport partition of the AnalyticsState table (see Runtime state reference). The cursor moves only when a cycle gets to the end. Until there is a cursor, for example on the first run, it reads whole days from the newest page view it has stored.
A failed day stops the cycle. When a day fails to download or to save, the importer stops there, and the next cycle retries it before any later day:
Failed saving data for day 2026-10-20 (attempt 1 of 3). Stopping this run here; the day will be retried next cycle, before any later day. AppInsightsDay: Day=2026-10-20; Outcome=Failed; Stage=<stage>; Attempt=1/3; ...
It says Failed downloading data when the download failed. The line ends with the chain of errors, and the exception itself is in the exceptions table. A cycle that stops early records neither FinishedSectionImport for the hits import nor FinishedImportCycle. Before there is a cursor, a cycle whose starting point has moved past the failed day says it is going back for it: Day 2026-10-20 failed in an earlier cycle; starting from it so it is retried before any later day.
After 3 failures in a row the importer stops forcing retries of that day, so one bad day can't hold up the import indefinitely, and carries on with later days:
Failed saving data for day 2026-10-20 again (3 failed attempts in a row). Stopped forced retries of this day and continuing with later days. What this cycle read for the day and did not save may not be read again automatically; ReadHitsDaysBeforeToday re-reads it. AppInsightsDay: Day=2026-10-20; Outcome=RetriesStopped; ...
It also sends one AppInsightsDayGivenUp custom event for that day. The attempt count is kept in memory, so restarting the web job starts it again from zero. This doesn't have to leave a gap: rows for that day that Application Insights ingests later are still read. But what the failed cycles read and didn't save isn't read again by itself. To find days given up on:
customEvents
| where name == "AppInsightsDayGivenUp"
| project timestamp, Day = tostring(customDimensions.Day), FailedStage = tostring(customDimensions.FailedStage),
Attempts = todouble(customMeasurements.Attempts)
| order by timestamp descTo recover a day the importer gave up on:
- Fix the cause shown in the error.
- On the App Service, set the application setting
ReadHitsDaysBeforeTodayto the number of days back to that day, for example5for a day five days ago. Changing the setting restarts the web jobs. While it's set, every cycle re-reads whole days from that many days ago and leaves the cursor where it is. Days that were already saved aren't duplicated. - Wait for a cycle to finish: an
AppInsightsImportCyclecustom event withOutcomeSucceededandModeBackfill. - Remove the setting, so that later cycles go back to reading from the cursor.
Only data that Application Insights still holds can be re-read, so a day older than its retention period can't be recovered.
An Application Insights query returns at most 500,000 rows, or about 100 MB, and marks anything bigger as partial.
The importer asks only for the columns and the event types it imports, and plans its queries from an hourly count of what there is to read, in windows meant to hold at most 20,000 rows each. When a window still comes back partial, it splits that window in half, and halves again, until every piece comes back complete:
Application Insights returned partial <type> results for <window> (the query reached its row or size limit). Querying each half of that window instead.
Read the <type>s for <window> in <N> pieces, as the window is over the Application Insights query limits.
<window> reads like 2026-10-20 09:00:00 to 2026-10-20 10:00:00 UTC, followed by the ingestion times it covers when the importer is reading from its cursor. Windows are never smaller than one minute: if even the smallest window comes back partial, the day's import stops with Application Insights returned partial <type> results even for the <N>-second window from <time> UTC, the smallest it is split into. The import is halted to avoid losing data., rather than save part of it.
- Clicks. Click times come from each browser's clock, so a click can reach Application Insights late, and a browser's clock can be wrong. The importer recognises a click it already has by its page view, link URL and time, so late clicks are imported and nothing is imported twice.
- Shared browser sessions. The tracker's session ID is a browser session cookie, so everyone using the same browser profile (a shared device, or someone switching accounts) shares it. The importer keeps one session per session ID and person, so everyone's page views and searches are kept.
- Untracked sites. Since Stable 1859 (PR #810): AITracker 1.6.4 and later (Stable 1859 ships 1.6.5) collect nothing while a modern page shows a site collection that the tracking extension isn't enabled on. SharePoint moves between site collections without reloading the page, so earlier versions kept sending that site's link clicks, and its address with the last tracked page's exit, to Application Insights. After upgrading, expect fewer clicks where people often browse from tracked sites to untracked ones.
Builds before 1856 dropped both. The fixes apply to what the importer reads from 1856 on; days it had already finished with aren't read again. See Known issues → Fixed issues.
If an error escapes a cycle, for example a SQL error loading the URL filters, the importer logs Import cycle failed: <error>. Trying again after the normal pause. and carries on: the AppInsightsImporter WebJob keeps running. That cycle doesn't record FinishedImportCycle. Fix the error it reports, especially if it repeats on every cycle.
Since Stable 1859 (PR #817). Builds up to and including 1857 store every page load time (dbo.hits.page_load_time) at a tenth of its real value: a page that took 2.5 seconds to load is stored as 0.25. Web activity, the SQL views and the Power BI reports all read that column, so they show pages loading ten times faster than they did. Stable 1859 stores the real time in seconds, so load times jump about tenfold for page views imported after the upgrade. That jump is the fix, not a slowdown.
Page views stored before the upgrade keep their old values until a DBA runs Repair.Page.Load.Times.sql, once, by hand. It is attached to Stable 1859 as a standalone script. GitHub replaced the spaces in its name with dots: in the repository it is src/Repair Page Load Times.sql. It's optional, and recommended if you keep page load history. It isn't a migration: nothing in the product runs it, and it doesn't change the schema. Its header is the full guide. In outline:
-
Upgrade first, so that new page views are stored correctly. Optionally, just before upgrading, stop the AppInsightsImporter WebJob and note the result of
SELECT MAX(id) FROM dbo.hits;. Use it as@LastLegacyHitId, and the time you stopped the importer as@CutoffUtc, for an exact boundary between old and new page views. -
Connect as the database's administrator (
db_owner): the script records its progress in extended properties on the database. -
Dry run. Set
@CutoffUtcto the start of the UTC hour in which the upgrade started (if unsure, earlier rather than later), leave@DryRun = 1, and run the script. It changes nothing. It reports how many page views it would repair, and the median load time per hour for the 24 hours either side of the cutoff. The median should jump about tenfold at the cutoff. If it jumps earlier, move@CutoffUtcback to the start of that hour; if it jumps later, move it forward. -
Repair. Set
@DryRun = 0and run the script again. It works in short batches and reports progress every 10 seconds, and the importers and the portal can keep running. If it stops part-way, run it again with the same@CutoffUtc: it carries on after the last batch it completed. Only one run works at a time: it holds an application lock on the database, so a second run started meanwhile stops at once, without changing anything. -
Refresh the Power BI datasets that read the database. Web activity shows the repaired times straight away. If you installed the optional
dbo.hits_PBItable, copy the repaired times into it with theUPDATEin the script's header.
The script multiplies by 10 each load time stored before the cutoff, so repaired times are accurate to 0.1 second. It leaves 0 (not measured), NULL and later page views alone. It runs only once: after it has completed, it refuses to run again, because a second run would make the times ten times too large. On a developer machine it repaired a table of 10 million page views in 42 seconds. On Azure SQL Database the service tier's log rate usually sets the pace: 100 million page views can take from about 17 minutes to an hour and a half. It needs no maintenance window.
The installer signs you in to SharePoint by opening your default web browser. This only happens when Web sessions is ticked under Workloads — with web tracking off, the SharePoint site list is used purely as a URL filter for the audit-log import and no sign-in is requested.
The sign-in reply comes back to a loopback address (http://localhost on an ephemeral port). If the installer machine blocks loopback traffic, or has no usable default browser, the sign-in never completes.
- Confirm a default browser is set and opens normally.
- Allow outbound HTTPS to
https://login.microsoftonline.com. - Allow loopback connections on the installer machine — see Prerequisites → Network & firewall rules.
By default the installer signs in using Microsoft's built-in SharePoint Online Management Shell application (client ID 9bc3ab49-b65d-410a-85ad-de819febfddc). These errors mean your tenant hasn't consented to it, has disabled it, or restricts which users may use it.
Fix it either way:
- Grant admin consent to the SharePoint Online Management Shell enterprise application in Entra ID, or
- Register your own public-client app and enter its ID in Sign-in app ID (optional) on the installer's SharePoint tab — see Optional: use your own Entra ID app registration.
The signed-in account must be a SharePoint Administrator (or Global Administrator) to upload and tenant-deploy to the app catalog, and a Site Collection Administrator on every target site. Sign in with an account holding both, or perform the app-catalog step manually — see Deploy Modern UI Extension to App Catalog.
If you administer the tenant as a guest (B2B) account, a 401/403 here usually means you were signed in against your home tenant rather than the target one. Set Sign-in tenant on the installer's SharePoint tab to the target tenant's directory ID or domain.
Since Stable 1859 (PR #808): a site that fails, for example with a 403 because the account isn't a Site Collection Administrator there, no longer stops the installation. Builds before 1859 end the run at the first failing site with FATAL: Unexpected error of type 'SpoRestException', and skip the remaining sites, the App Service warm-up and the WebJob checks. From 1859 the installer logs an error naming the site and SharePoint's reason, carries on with the other sites and the rest of the run, then lists every site it missed: The AITracker was installed on 2 of 4 SharePoint site collection(s). It was NOT installed on: …. The run ends Completed with N error(s), M warning(s). Fix the problem logged for each of those sites, then run the installer again.
Fixed in Stable 1859 (PR #808). On a site collection with sub-webs, every run of an earlier installer removed the root web's AITracker registration, so the root web's pages stopped being tracked, and added another registration to each sub-web. From 1859 each run replaces each web's own registration, and uninstalling removes the sub-webs' registrations too.
Fix: re-run the installer's SharePoint step. Run the 1859 installer with Update solution with latest release selected, and sign in to SharePoint when asked. That run puts the root web's registration back and removes the duplicates, at a few seconds per sub-web. See Known issues → Fixed issues.
Your app registration is single-tenant (the default for a new registration), so it can't use the shared multi-tenant sign-in endpoint. Fill in Sign-in tenant next to the app ID. The installer now validates this before you install, so you should only see this on an older build or a hand-edited config file.
SharePoint processes app-catalog uploads asynchronously, so a package can be accepted but not yet be deployable. The installer waits and retries for up to 50 seconds. If it gives up, the log says the app "was uploaded … but SharePoint hadn't made it available" — the package is in the catalog, so just deploy it by hand from the app catalog (tick "Make this solution available to all sites"), then re-run the installer if the per-site tracker install didn't complete.
The site collection has custom scripts disabled, so the installer can't register the classic-page custom action. Run:
Set-SPOsite https://contoso.sharepoint.com/sites/corp -DenyAddAndCustomizePages 0Modern pages are unaffected — they are tracked by the SPFx extension, which doesn't need custom scripts.
Older releases: MFA used to break this stage entirely (
IdcrlException: The sign-in name or password does not match one in the Microsoft account system) because the installer used an embedded Internet Explorer cookie login. That is fixed — MFA, Conditional Access, passkeys and FIDO2 all work with the browser sign-in. The old error and its workarounds are kept on Legacy SPO web setup.
Page properties reach SQL in two throttled steps, described in Product notes → Page metadata updates. A visiting browser reads a page's properties at most once per refresh cadence (24 hours by default) and sends them to Application Insights, and the importer refreshes a page's metadata in SQL at most once per cadence. So first check that the page has been visited since its properties changed, and allow for the cadence.
If a property you expect still doesn't arrive, check what the tracker reads:
- Open the page with the browser's Developer Tools on the Network tab.
- Find the request ending
/_api/web/lists/getbytitle('<list>')/items(<id>)/properties. Its response holds the properties the tracker sends for the page: check that the field you expect is in it. Values over 1,000 characters are discarded. - If there's no such request, this browser has already sent the page's properties within the cadence. Confirm the tracker is loaded, then delete the
AITrackerPagesMetadataUploadedentry under Developer Tools → Application (or Storage) → Local storage for the site's origin, and reload the page. It is browser local storage, not a cookie.
Don't share the raw property response: it can contain site URLs, IDs, page content and user-authored values.
Return to Troubleshooting · All topics in this guide
Previous: Usage Reports and Email Troubleshooting · Next: Web Portal Troubleshooting
- Copilot analytics overview
- Copilot Adoption user guide
- Copilot Adoption setup (admins)
- Work Trend Index
- Copilot investment
- Copilot spend
- Agent costs
- Prompt categories
- Copilot credit history (preview)
- Agent 365 package catalog
- The Copilot data in depth
- Prerequisites
- App registrations setup
- Install with the installer
- Verify the deployment
- Security & permissions model
- Private endpoints (optional)
- Certificate authentication (optional)
- Manual installation
- Activity & usage analytics (Power BI)
- SharePoint page tracking