When the Map Is Wrong
This page is a reference sheet, not a lesson. It is organised by what the operator or the tester sees, because that is how the report arrives, and each row names the first thing to check and the page in this chapter that explains the mechanism. The second half is the checklist for the changes that are not code at all: portal edits, environment variables and credentials.
Nothing, or a loader forever
| Symptom | First check |
|---|---|
| Loader never clears, no map | Network: /api/arcgis/token. A 500 is missing credentials; a pending request is Key Vault access. |
| Loader clears, grey map, "could not be loaded" | usePreparedWebMap failed to load() the WebMap: wrong portal item id, or the item is not shared with the app's OAuth identity. |
| Map renders on desktop, invisible on a phone | Not a load failure. Compare the overlay's computed z-index with the drawer's; the singleton is positioned behind an opaque paper. |
| Map mounts at a tiny size and never recovers | A slot registered at 0 × 0. The provider waits for a non-zero rect by design; something bypassed it. |
| Two "FeatureMap initialized" log lines | The element was reparented. Portals and appendChild re-create the view. |
Layers missing or misbehaving
| Symptom | First check |
|---|---|
| Error 499 "Token Required" on layer requests | A map rendered before isReady, or the layer's server is not in ARCGIS_AUTHENTICATED_SERVER_URLS. Never fix it by swallowing the error in the interceptor. |
| One portal layer absent, everything else fine | Console: "Suppressed credential challenge for unknown server". Add the FeatureServer base URL to the whitelist, without the trailing layer index. |
| Layer row shows "!" and never loads | Its service failed once; load() is one-shot per instance. Reload the page to retry; if it persists, the service is down or not shared. |
| A client layer appears in the Layers Menu | Its title is not in CLIENT_LAYERS. |
| Basemap renders but no cameras at all | The WebMap has no Velocity Locations layer; one error line in the console names the item. Point the env at a WebMap that carries the set. |
| Layers vanish from the menu right after an edit | Fast Refresh of the catalog module orphaned the registry. Full reload. |
| Legend is empty or missing restored layers | The legend is behind enable-map-legend-button; its allow-list comes from the catalog and skips layers without a layer view yet. |
Arrows, lines and wedges
Camera points, arrows, wedges and beams are the Velocity layers' own; the rows below that mention arrow
builds, PTZSnapshot, LOS colour or clusters describe the legacy client layers removed in October 2026
and are kept for anyone reading an old log.
| Symptom | First check |
|---|---|
| Arrows in the Gulf of Guinea (0°N 0°E) | Operational data arrived before static data; coordinates fell back to parseFloat('0'). |
| Arrows never rotate | p arriving as a string or not in degrees; the rotation variable reads the p field directly. |
| Arrow stays black during an AI hit | User lacks AI.Access, or the detection kind is switched off in AI settings (isEffectiveAiHit). |
| Arrow state lags a stale image by up to a minute | Timestamp-only changes under 60 s are ignored on purpose. |
| A new arrow state never updates | The field is not in PTZSnapshot, so the diff cannot see it change. |
| Arrows vanish when zooming out | Working as designed past scale 3,000,000 unless isRecentlyMoved is 1. |
| Duplicate arrows after a source switch | Two arrow builds overlapped; the in-flight guard should prevent it, so look for a new caller of the build. |
| LOS lines all grey | time missing from the record; the colour reads the last-movement time, not the image time. |
| Wedge or line starts at the wrong distance after a zoom | The stationary refresh did not fire; check that the selection hook is enabled and the source is client. |
| Cursor line blinks or lags | It must stay on the GraphicsLayer with in-place geometry; a feature-layer edit per move was the old bug. |
| Clusters never disappear when zooming in | maxScale on the FeatureReductionCluster is CLUSTER_DISABLE_SCALE (100,000); confirm the reduction was built by createClusterConfig, not hand-assigned. |
Real-time source
| Symptom | First check |
|---|---|
| No cameras on the map at all | Discovery found no Locations layer (one error line names the WebMap). Check the service name matches <ENV>_Camera_Locations_Persistence / _Status_Persistence. |
| Layers load, no points at all | The claimed Locations service failed within 10 s and no fallback existed; look for "is unusable" in the console. |
| Points show, beams never appear | A StreamLayer only holds features received after connect; the beam appears on the camera's next publish. Hover a camera that moved recently. |
| Beams for every camera at once | The beam layer-view filter is missing; the only writer is applyVelocityLayerFilters. |
| Scoped-out cameras still visible under real-time | The area clause is not reaching the stream; check mapAreaWhere on the area store, never layer.filter. |
| Both a persisted and a live beam for one camera | The handoff did not fire; confirm data-received events carry drawable geometry (isDrawableStreamGeometry). |
| Stream never connects, row spins | Stale token on customParameters; a refreshed token is re-injected on view-ready and at prepare time only. |
Map disagrees with the database
| Symptom | First check |
|---|---|
| Stream beam points the wrong way, or no cone | The esri_cameras hub: ImageAcquisition.ESRI.CameraStatus.Submission by outcome. A status event carries no hfov; the cone waits for the next position event. |
| AI hit lines stopped after a deployment | AiIngestion.ActivePath: which Notification lane is live. With UseEventHub on, Velocity reads condor_hub itself and Notification publishes nothing to esri_velocity. |
| Alert moved in the app, still at the old point on the map | The alert update is best effort: SQL first, Esri on a query + updateFeatures that logs and returns on failure. Look for the "Failed to sync updated alert" error. |
| Camera_Locations has a camera twice, or missing | Three writers share one upsert; run the Admin full sync to rebuild. If the full sync fails at its token step, the public site's /api/arcgis/token is the dependency. |
| PulsePoint incident lingers as active | The daily cleanup runs in production only, at 13:00 UTC; the Admin reconcile marks features with no Cosmos record inactive. |
Interaction
| Symptom | First check |
|---|---|
| Hovering a site fires feature queries | The camera layer lost outFields: ['*']; the fallback query is running on every hover. |
| Clicking a site opens one camera, not the site | Point hits must beat arrow hits; check the order in the click handler. |
| Map ignores camera clicks | A pick mode is still armed (area or coordinates). Both disarm on Escape and on map unmount. |
| Alert modal closes itself while editing a pin | isAlertEditActive guard missing from a new click path. |
| Chip or callout drifts or has no styling | It is rendered inside the <arcgis-map> shadow root; portal into the "Feature Map" region. |
| Public visitor lands looser than a shared link | The MOU zoom cap (zoom 16) clamped the goTo; the toast says so. |
Request budget
| Symptom | First check |
|---|---|
| ArcGIS 429s climbing in Kusto | Extract httpStatus from AppExceptions.OuterMessage in the workspace; rank by service; look for a new per-event query. |
| Startup issues a hundred ArcGIS requests | Layers are not being parked; confirm <arcgis-map> receives map= and not item-id. |
| Build agent killed during the image build | A new import() into the SDK; route through loadArcgisOnDemand. |
Portal-side and environment changes
Where the mechanism is explained
Boot and tokens: The Token Handshake and One WebMap, One View, One Lifetime. Layers: The Parking Lot. Arrows and lines: Icons Without Image Files and From Poll to Pixel. Real-time: Streams, Beams, and the Handoff. Clicks: Hit Tests and the Click Budget. Requests: The Request Budget.