Why Windows Search Freezes for 30 Seconds After Every Reboot

WINDOWS INTERNALS MINIFILTERS FRIDA DROPBOX
2026-04-15

Windows Search on four different Windows 11 machines was broken after every reboot — a 30-second blank window before results appeared. Different hardware, different user profiles, same symptom. Registry hacks, index rebuilds, driver updates, none of it helped. Custom Frida instrumentation on 46 SearchHost threads uncovered a cascade of four interacting bottlenecks — ending with a kernel minifilter deadlock between the search indexer and Dropbox that nobody had documented before. The common denominator across all four machines was Dropbox with Smart Sync enabled. The mitigations below reduce the stall but don't eliminate it; a second failure mode — orphan sync root registrations left behind by profile migrations — keeps the deadlock alive long after boot. The durable fix is further down: a replacement search engine that never opens a file.

Background

The primary test machine: Windows 11 Pro, build 26100, Ryzen 5 2400G, 14 GB RAM. But the same issue reproduced on three other Windows 11 PCs — a laptop, a workstation, and a family member's desktop. All different hardware, different Windows builds, different user profiles. The one thing they shared: Dropbox with Smart Sync.

The symptom was identical everywhere: hit Win+S, type something, stare at a blank pane for 20–30 seconds. Then suddenly results appear, and search works fine until the next reboot. Rebuilding the index, disabling Bing, re-registering AppX packages, resetting the WSearch service — the standard fixes from every forum thread did nothing. The problem always came back after reboot.

The breakthrough came from an observation on the primary machine: search started working exactly when the Dropbox tray icon finished loading. Every time. Once noticed, the same correlation was confirmed on the other three machines. That pointed to a boot-time dependency nobody was looking at.

Building the Instrumentation

Standard troubleshooting was useless because the symptom (blank search) has a hundred possible causes. I needed to see exactly what SearchHost.exe was doing during those 30 seconds. The approach:

  1. A Python tool using Windows APIs (NtQueryInformationThread, EnumProcessModulesEx, MiniDumpWriteDump) to enumerate all SearchHost threads, map their start addresses to loaded modules, and create a minidump for offline analysis
  2. Frida hooks (using Process.getModuleByName().getExportByName() — the static Module.getExportByName() is gone in Frida 17) on blocking calls: WaitForSingleObject, WaitForMultipleObjects, MsgWaitForMultipleObjectsEx, NtAlpcSendWaitReceivePort, CoCreateInstance, NdrClientCall3
  3. WPR (Windows Performance Recorder) with custom ETW profiles targeting Microsoft-Windows-Search-Core and related providers

The Python tracer kills SearchHost (simulating a cold boot), waits for it to respawn, attaches Frida, triggers Win+S via keybd_event, types a query via SendKeys, and records every blocking call per-thread for 20 seconds.

Thread Analysis Output (cold start)
Thread categories:
  ThreadPool     : 16 threads   // ntdll!TppWorkerThread
  unknown        : 7  threads   // shcore, tquery, CoreMessaging
  RPC/COM        : 2  threads   // combase.dll marshaling
  Graphics       : 2  threads   // atidxx64.dll + directmanipulation
  SearchUX       : 1  thread    // SearchHost.exe main
  XAML           : 1  thread    // Windows.UI.Xaml.dll
  WebView2       : 1  thread    // EmbeddedBrowserWebView.dll

Top blocking threads:
  TID 8380:  12,597ms  shcore.dll+0x47c50    // UI message pump
  TID 13628: 8,445ms   tquery.dll+0x153350   // search query engine

Two threads account for 21 of 22 seconds of total blocked time. The other 28 threads are either idle thread pool workers or waiting on these two. The 16 thread pool threads are a red herring — standard Windows TP workers, mostly asleep.

The Minifilter Stack

To understand the root cause, you need to understand how file I/O works on Windows. Every ReadFile call passes through a stack of minifilter drivers, each at a specific altitude (Microsoft's actual term, not mine). Higher altitude = intercepts first:

fltmc output — minifilter altitudes on the test machine
Filter Name          Altitude    Purpose
───────────────────  ──────────  ─────────────────────────────
bindflt              409800      Container/virtualization binding
UCPD                 385250      User-mode crash protection
WdFilter             328010      Windows Defender (antivirus band)
storqosflt           244000      Storage QoS
dbx                  186500      Secure Boot revocation
CldFlt               180451      Cloud Files (Dropbox, OneDrive)
bfs                  150000      Basic filesystem
Wof                   40700      Windows Overlay Filter (compression)
FileInfo              40500      File information/metadata

The metaphor: an I/O request is a ball falling from userspace to disk. Each filter is a net stretched across at a certain height. Higher altitude catches the ball first. Microsoft assigns altitude ranges by category — antivirus gets 320000–329999, cloud storage gets 180000–189999. You must register your altitude with Microsoft; no approval, no altitude.

The altitude is stored in the registry at HKLM\SYSTEM\CurrentControlSet\Services\{FilterName}\Instances\{Instance}\Altitude.

Bottleneck 1: Defender I/O Amplification

WdFilter.sys at altitude 328010 intercepts every file open/read/write from every process. When SearchIndexer reads a file for content indexing:

I/O amplification per indexed file
SearchIndexer ReadFile("main.rs")
   WdFilter intercepts, sends to MsMpEng for scanning    // I/O #2
   Defender approves, original read completes
   SearchFilterHost extracts text content
   Writes extracted text to Windows.db                    // I/O #3
   WdFilter intercepts the DB write too                   // I/O #4

Result: 4× I/O amplification per file indexed

With thousands of .rs, .js, .py files in dev repos, this creates sustained disk thrashing. Defender was accumulating 270 seconds of CPU while SearchIndexer was at 250 seconds — both hammering the same files simultaneously.

On this machine, Defender exclusions existed for C:\Users\Marty\ (old profile) but not C:\Users\marty.CHOPIN\ (current profile). Every dev file was being double-scanned.

Fix: Add-MpPreference -ExclusionProcess SearchIndexer.exe breaks the chain. Defender drops from hammering CPU to 0.02s over 3 seconds.

Bottleneck 2: The 1.6 GB Search Index

Windows 11 stores the search index in C:\ProgramData\Microsoft\Search\Data\Applications\Windows\Windows.db — a SQLite database. On this machine: 1,641 MB. Normal is 100–300 MB.

The bloat was caused by full-text content indexing of dev repos: every .rs, .js, .json, .py file's contents stored in the DB. The Frida trace showed tquery.dll (Microsoft Tripoli Query Engine) blocking for 8.4 seconds in a single WaitForMultipleObjects call while scanning this massive DB.

Fix: register the null persistent handler ({098f2470-bae0-11cd-b579-08002b30bfeb}) for code extensions. This tells SearchFilterHost to index filenames only, skip content extraction. Files are still findable by name; the DB drops from 1.6 GB to ~50 MB.

Bottleneck 3: WebView2 Cold Start

Windows 11's search UI is a web app. SearchHost.exe renders it inside an embedded Chromium browser via WebView2. Every boot, it spawns 6 msedgewebview2.exe processes consuming ~370 MB:

ProcessPurpose
BrowserMain coordinator, manages WebView2 lifecycle
GPUD3D/DirectX compositing for the search UI
RendererExecutes the JS search app from Cortana.UI/cache/WV2Local/
UtilityNetwork/services
CrashpadChromium crash handler
Spare rendererPre-spawned for next navigation

The Frida trace caught SearchHost polling registry keys in a loop during initialization:

Registry polling during WebView2 init (Frida RegQueryValueExW hook)
// Polled 21+ times each during the 5-second init window:
IsWebView2           OK    // "is WebView2 ready?"
SnrBundleVersion     OK    // "is the JS bundle loaded?"
WebView2Version      OK    // version check
MsbBundleVersion     MISS  // dead feature flag, fails every time
BINGIDENTITY_PROP_AUTHORITY OK  // Bing identity (even with Bing disabled!)

There is no registry key or ViVeTool feature ID that disables WebView2 in SearchHost on build 26100. IsWebView2=0 gets immediately overridden by the process. The XAML fallback was removed. This is a fixed ~5 second cost.

Bottleneck 4: The CldFlt Deadlock (Root Cause)

The previous three bottlenecks added up to maybe 15 seconds. The remaining time — and the reason it correlated with Dropbox — came from the cloud files minifilter.

Dropbox Smart Sync uses the Windows Cloud Files API (cfapi) to show placeholder files. These files appear in the filesystem with their original names and sizes, but have no local content. Every file has FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS (0x400000) set — reading the file content triggers a download through CldFlt.sys.

Cloud file state check (PowerShell)
# Check Dropbox placeholder state
$ Get-ChildItem "$env:USERPROFILE\Dropbox" -Recurse -File |
    Select -First 200 | ForEach {
      [uint32][IO.File]::GetAttributes($_.FullName) -band 0x400000
    } | Group | Select Count, Name

Count  Name
200    True     # ALL 200 sampled files: RECALL_ON_DATA_ACCESS

On this machine: 9,234 Dropbox files, every single one a placeholder.

The Deadlock Chain

At boot, WSearch starts before Dropbox finishes loading. SearchIndexer crawls the Dropbox folder and tries to extract content from each file for full-text indexing. Here's what happens in the minifilter stack:

I/O path through the minifilter stack
SearchIndexer ReadFile("proposal.docx")
   409800  bindflt        (pass-through)
   385250  UCPD           (pass-through)
   328010  WdFilter       Defender scans → MsMpEng re-reads
   244000  storqosflt     (pass-through)
   180451  CldFlt         "This is a placeholder!"
                              → issues CF_CALLBACK_TYPE_FETCH_DATA
                              → sends callback to Dropbox sync provider
                              → Dropbox is still starting
                              → no provider connected to CldFlt
                              → I/O BLOCKS (up to 60s timeout)
   150000  bfs            (never reached while blocked)
           NTFS / Disk    (never reached)

SearchIndexer.exe lives under %systemroot%, which means it bypasses the cloud file access denial (STATUS_CLOUD_FILE_ACCESS_DENIED, 0xC000CF18) that blocks most services from triggering hydration. It's "privileged" enough to trigger a download on every placeholder file.

The CldFlt FETCH_DATA callback has no receiver because Dropbox hasn't connected to the minifilter yet. Each blocked I/O can wait up to 60 seconds before timing out. With SearchFilterHost spawning threads for multiple files simultaneously, the thread pool saturates. This is why the Application Event Log showed 66 Event ID 10024 entries: "The filter host process did not respond and is being forcibly terminated."

The deadlock resolves itself when Dropbox finishes starting and connects its sync provider to CldFlt. Pending callbacks get serviced, blocked threads unblock, and search starts working. The user sees this as: search works as soon as the Dropbox tray icon appears.

The Fix

The fix targets all four bottlenecks:

BottleneckFixSurvives reboot?
CldFlt/Dropbox deadlock attrib +I on Dropbox folder (NTFS NOT_CONTENT_INDEXED) Yes (NTFS metadata)
Defender I/O amplification Add-MpPreference -ExclusionProcess SearchIndexer.exe Yes
1.6 GB index bloat Null persistent handler on 60 code extensions Until Windows Update
WebView2 cold start Scheduled task pre-warms search at login Yes

The critical fix is the first one. FILE_ATTRIBUTE_NOT_CONTENT_INDEXED (0x2000) tells SearchIndexer: index filenames and metadata (which doesn't trigger hydration), but skip content extraction (which does). Dropbox files are still findable by name. The attribute is NTFS metadata — it survives reboots, index rebuilds, and Windows Updates.

The one-line fix
attrib +I "C:\Users\%USERNAME%\Dropbox" /S /D

Stronger alternative. attrib +I tells the indexer to skip content extraction, but the tree is still enumerated and every placeholder's metadata is still touched on each incremental crawl — enough to keep the cldflt probe path warm. A Crawl Scope Manager URL exclusion (ISearchCrawlScopeManager::AddUserScopeRule with fInclude=0) removes the Dropbox tree from the indexer's scope entirely: no walk, no metadata probe, no CF_CALLBACK_TYPE_FETCH_DATA. attrib +I is per-file and propagates automatically to files Dropbox creates later; a CSM exclusion is set-and-forget at the path level. Both are covered in more detail below.

Update: The Orphan Sync Root Variant

The mitigations above were applied on 2026-04-15 and the boot stall appeared resolved. Three days later the same machine regressed: Win+S back to 30-second blanks, even with Dropbox already running for hours. Not a boot-timing issue this time. The cause was a stale sync root registration from a profile migration.

When a Windows account is renamed or re-created, Windows keeps the old profile directory on disk — and keeps the old Dropbox sync root registration in the registry. On this machine an old Marty account had been replaced by marty.CHOPIN (the .CHOPIN suffix is what Windows appends when an existing profile folder is still present). Two artifacts survived: the C:\Users\Marty\ directory tree, and the sync root entry under SyncRootManager:

HKLM SyncRootManager entries (before cleanup)
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager\
  Dropbox!S-1-5-21-1443736254-2868974255-1052510127-1000!dbid:AAAfD...   ← ghost SID
  Dropbox!S-1-5-21-1443736254-2868974255-1052510127-1003!dbid:AAAfD...   ← current user

SID -1000 was not in ProfileList — the profile itself had been deleted, only the sync root registration survived. When cldflt resolves a placeholder under the ghost tree, it consults the sync root table, finds a registered provider for C:\Users\Marty\Dropbox\*, and issues a CF_CALLBACK_TYPE_FETCH_DATA to a provider that no process will ever connect. This is a permanent version of the boot-time deadlock. The original cascade resolves when Dropbox connects to CldFlt; the orphan variant never resolves, because there is no provider coming. Users see it as "Windows Search randomly stopped working again," and nothing about it is random.

Every subfolder of the ghost tree was a cloud-files reparse point — reparse tag 0x9000601A, IO_REPARSE_TAG_CLOUD_A — so any enumeration from Explorer, Defender, or SearchIndexer that crossed the tree handed the I/O to cldflt, which blocked on the orphan provider lookup. Deleting files via Explorer took minutes per file for exactly this reason: the shell asks the cloud provider to dehydrate before unlinking, and no provider answers.

Two-step fix

Cleanup (PowerShell, elevated)
# Step 1: remove the orphan Dropbox tree via native rd
# (Explorer delete hangs on cloud-placeholder dehydration callbacks; rd bypasses the shell)
cmd /c "rd /s /q C:\Users\Marty\Dropbox"

# Step 2: unregister the ghost sync root - the step most writeups miss
Remove-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager\Dropbox!S-1-5-21-1443736254-2868974255-1052510127-1000!dbid:*" -Recurse

# Optional: restart the indexer so cldflt sees the cleaner view immediately
Restart-Service WSearch -Force

Step 1 alone is insufficient. Deleting the folder reclaims disk space but the sync root registration still exists, and cldflt still has a table entry pointing at a path whose placeholders can no longer resolve. Step 2 is what actually stops the blocking. Any machine that has gone through a profile rename is a candidate for this variant, independent of whether the original boot cascade ever fired.

A CSM exclusion as belt-and-suspenders

Whether the stall is the boot cascade or the orphan sync root, excluding the Dropbox tree from the indexer's scope prevents either pattern from running. The documented path is ISearchCrawlScopeManager::AddUserScopeRule, but on Windows 11 build 26100 the interface chain is harder than it looks: ISearchManager::GetCatalog returns an object whose QueryInterface refuses both the published ISearchCatalogManager IID (AB310581-AC80-11D1-8DF3-00C04FB6EF6A) and the v2 IID (7D722555-D82F-4FEC-B511-AD5BB0237CD7) with E_NOINTERFACE. Windows has rolled the interface forward to a GUID that isn't yet in the public headers.

The fallback is a direct registry write to HKLM\SOFTWARE\Microsoft\Windows Search\CrawlScopeManager\Windows\SystemIndex\WorkingSetRules\N with URL, Include=0, Default=0. The key's DACL is locked to TrustedInstaller — elevated administrators cannot open it for write even after enabling SeTakeOwnershipPrivilege, and Get-Acl itself refuses to read the security descriptor. The working approach: dispatch the write from a one-shot schtasks task running as NT AUTHORITY\SYSTEM, which has the raw ACL rights the key demands.

Writing a TrustedInstaller-locked Search key from SYSTEM
# Write the payload that runs inside the SYSTEM task
@'
$ws = 'HKLM:\SOFTWARE\Microsoft\Windows Search\CrawlScopeManager\Windows\SystemIndex\WorkingSetRules'
$n  = ((Get-ChildItem $ws).PSChildName | ForEach {[int]$_} | Measure-Object -Max).Maximum + 1
$k  = "$ws\$n"
New-Item -Path $k -Force | Out-Null
Set-ItemProperty -Path $k -Name URL     -Value 'file:///C:\Users\<you>\Dropbox\*' -Type String
Set-ItemProperty -Path $k -Name Include -Value 0 -Type DWord
Set-ItemProperty -Path $k -Name Default -Value 0 -Type DWord
'@ | Set-Content C:\Windows\Temp\csm-exclude.ps1

# Register, trigger, clean up
Register-ScheduledTask -TaskName "TmpCsmExclude" -Force `
  -Action    (New-ScheduledTaskAction    -Execute powershell.exe -Argument "-File C:\Windows\Temp\csm-exclude.ps1") `
  -Principal (New-ScheduledTaskPrincipal -UserId SYSTEM -RunLevel Highest -LogonType ServiceAccount) `
  -Trigger   (New-ScheduledTaskTrigger   -Once -At (Get-Date).AddYears(1))
Start-ScheduledTask -TaskName "TmpCsmExclude"
Start-Sleep -Seconds 3
Unregister-ScheduledTask -TaskName "TmpCsmExclude" -Confirm:$false
Restart-Service WSearch -Force

Once the rule is in WorkingSetRules, the crawl scope manager treats the Dropbox tree as out of scope for all future queries. The walk never starts, cldflt is never consulted for placeholders under that path, and neither the boot cascade nor the orphan-sync-root variant can fire. This buys stability until the replacement engine described below is fully installed. A Dropbox folder excluded this way is no longer searchable via Win+S — which is the core reason replacement rather than exclusion is the direction the work ultimately took.

The Full Cascade

What makes this bug hard to diagnose is that it's not one problem — it's four independent issues that amplify each other:

Boot timeline
t=0s   Windows boots
t=2s   WSearch service starts (Automatic)
         SearchIndexer begins crawling user profile
         Hits Dropbox folder: 9,234 placeholder files
t=3s   SearchFilterHost tries content extraction
         CldFlt blocks → Dropbox not ready → threads hang
         WdFilter scans each file Defender tries to read → more blocking
t=5s   FilterHost threads pile up, start getting killed (Event 10024)
         Meanwhile: SearchHost spawning 6 WebView2 processes (370 MB)
         WebView2 polling IsWebView2 / SnrBundleVersion in a loop
t=8s   WebView2 initialized, but no search results available
         tquery.dll waiting on index that's being rebuilt
         Windows.db growing (content extraction still running on non-Dropbox files)
t=25s  Dropbox tray icon appears → provider connects to CldFlt
         Blocked I/O unblocks → pending FilterHost work completes
t=30s  Search starts working

Sidebar: The Altitude Arms Race

The minifilter altitude system is a "gentleman's agreement" enforced by Microsoft's altitude registration process. It works because everyone plays by the rules at the filter stack level. The real arms race — particularly in anti-cheat vs cheat — happens below:

The privilege ring escalation ladder
Ring  3    Userspace  apps, cheats (DLL inject, memory edit)
Ring  0    Kernel     drivers, anti-cheat, minifilters (WdFilter, CldFlt)
Ring -1    Hypervisor VT-x / AMD-V (Hyper-V, Riot Vanguard)
Ring -2    SMM        System Management Mode (Domas "Memory Sinkhole" 2015)
Ring -3    ME / PSP   separate CPU, own OS, DMA to everything

Ring -3 is Intel Management Engine (a full Minix 3 OS running on a separate x86 core inside every Intel CPU since 2008) or AMD Platform Security Processor (ARM Cortex-A5 with TrustZone). Both have DMA access to all system memory, run when the PC is "off," and execute firmware signed by the vendor's RSA key. The chipset encryption key was extracted in 2020 (CVE-2019-0090), so the firmware can be read, but the signing key has not been found — modified firmware won't execute.

Positive Technologies also discovered a hidden HAP (High Assurance Platform) bit that disables most ME functionality — traced to an NSA program. The NSA asked Intel for a kill switch for their own machines while everyone else runs ME unmodified.

How Everything Search Reads the MFT

The previous section showed that Everything bypasses the minifilter stack entirely. Here is the exact mechanism, because understanding it matters for building alternatives.

Everything opens the volume directly with CreateFile("\\\\.\\C:", ...), obtaining a volume handle — not a file handle. It then calls DeviceIoControl with FSCTL_ENUM_USN_DATA to walk the NTFS Master File Table record by record. Each record returns three fields: FileReferenceNumber, ParentFileReferenceNumber, and FileName. That is the complete dataset — metadata only, no file content is read at any point.

MFT enumeration via FSCTL_ENUM_USN_DATA
// Open volume handle (not a file handle)
HANDLE hVol = CreateFileW(
    L"\\\\.\\C:",
    GENERIC_READ,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_EXISTING, 0, NULL
);

// Walk the MFT record by record
MFT_ENUM_DATA_V0 med = { 0, 0, maxUsn };
while (DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA,
        &med, sizeof(med), buf, bufSize, &bytesReturned, NULL)) {
    // Each USN_RECORD contains:
    //   FileReferenceNumber       — unique MFT index
    //   ParentFileReferenceNumber — parent directory MFT index
    //   FileName                  — file/directory name
    // NO content read. NO file handle opened. NO minifilter traversal.
}

The distinction is critical: minifilters like WdFilter and CldFlt register callbacks on IRP_MJ_CREATE (file open) and IRP_MJ_READ (file read) for individual file handles. A FSCTL_ENUM_USN_DATA call on a volume handle is a device I/O control request to the NTFS driver. It is not IRP_MJ_CREATE. The entire filter stack — every minifilter at every altitude — is bypassed. No Defender scan. No CldFlt hydration check. No content extraction. The NTFS driver reads its own internal data structures and returns the results directly.

For real-time updates after the initial scan, Everything calls DeviceIoControl(FSCTL_READ_USN_JOURNAL), which returns a stream of change records (file created, renamed, deleted) as they happen. This too operates on the volume handle, not on individual files.

As the Everything developer (voidtools handle "void") explained on the voidtools forum: FSCTL_ENUM_USN_DATA "does not walk through the change journal — this call walks through the MFT to identify files." The change journal is a separate structure that records modifications; the MFT is the filesystem's master allocation table. Everything reads the allocation table directly, then subscribes to the journal for incremental updates.

The performance difference is staggering. A fresh Windows 11 install has roughly 120,000 files. Everything indexes them all in approximately 1 second. Windows Search takes minutes to hours, because it opens every file individually, passes each through the minifilter stack, extracts text content via IFilter plugins, and writes the results to a SQLite database — which itself passes through the minifilter stack on every write.

Everything Windows Search
Initial scan ~1 second (120k files) Minutes to hours
Content indexing None (filenames only) Full text via IFilter plugins
Minifilter interaction None (volume-level FSCTL) Every file traverses full stack
Hydration trigger Never (no file open) Every cloud placeholder
Defender interaction None Scans every file opened + every DB write
Database format Custom binary (typically <50 MB) SQLite (Windows.db, often >1 GB)
Update mechanism USN journal monitoring (FSCTL) Filesystem change notifications + periodic recrawl

Methodology

Environment: Windows 11 Pro 10.0.26100, Ryzen 5 2400G (Vega 11), 14 GB RAM, Dropbox with Smart Sync (9,234 cloud-only files)

Tools used:

  • Frida 17.9.1 (Python bindings) — runtime instrumentation of SearchHost.exe. Hooks on WaitForSingleObject, WaitForMultipleObjects, MsgWaitForMultipleObjectsEx, NtAlpcSendWaitReceivePort, CoCreateInstance, CreateFileW, RegQueryValueExW, CreateProcessW, LoadLibraryExW
  • Custom Python tooling (ctypes) — NtQueryInformationThread for thread start addresses, EnumProcessModulesEx for module mapping, MiniDumpWriteDump for offline analysis
  • WPR (Windows Performance Recorder) — kernel ETW tracing with custom profile targeting Search-Core, Search-UI, and RPC providers
  • PowerShell — process monitoring, registry analysis, NTFS attribute manipulation, Defender configuration, scheduled task management
  • fltmc — minifilter enumeration and instance mapping