“AppDomain … created” in the SQL Server error log — what it means and how to troubleshoot

Example message
AppDomain 3 (master.sys[runtime].2) created.

What this message means (plain English)

SQL Server hosts the .NET Common Language Runtime (CLR). When SQL Server needs CLR—for a user assembly or for certain built-in features—it spins up an AppDomain, which is an isolation container inside the SQL Server process where CLR code runs.

That log line says:

  • AppDomain 3 → the internal ID SQL Server assigned to this domain.
  • (master.sys[runtime].2) → the database and owner that the domain is associated with (here, system-owned objects in master).
  • created → SQL Server just initialized this CLR sandbox so something could run in it.

This is informational, not an error. You’ll often see a matching “… unloaded.” later when SQL Server tears it down.

What typically triggers AppDomain creation

  • First use of SQLCLR objects in a database (CLR functions, procs, triggers, user-defined aggregates/types).
  • Use of features implemented with CLR (e.g., geography/geometry/hierarchyid, Policy-Based Management pieces in msdb, some internal system tasks).
  • Server startup, database restore/attach, or the first time a database is accessed after restart.
  • Toggling clr enabled or deploying/dropping assemblies.

If you only see a handful of these around startup or when you first call a CLR routine, that’s normal. If you see hundreds of create/unload cycles, that’s a smell you should investigate.


Quick decision guide

  • One-off at startup or first use? Safe to ignore.
  • Repeated create/unload all day long? Investigate (auto-close, memory pressure, assembly churn, or misconfigured CLR security).
  • Failures right after creation? Look for additional messages about assembly load or permission problems.

How to research & troubleshoot (step-by-step, with scripts)

1) Confirm CLR state and basic facts

-- Is CLR enabled at the instance level?
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'clr enabled';

-- CLR properties (version, memory)
SELECT * FROM sys.dm_clr_properties;

-- What CLR app domains exist right now?
SELECT * FROM sys.dm_clr_appdomains;

-- Which assemblies are currently loaded?
SELECT * FROM sys.dm_clr_loaded_assemblies;

-- Memory used by SQLCLR
SELECT type, pages_kb
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%CLR%';

What to look for

  • clr enabled should be 1 if you use user assemblies.
  • sys.dm_clr_appdomains shows the friendly name (often db.owner[runtime].N), creation time, and state.
  • sys.dm_clr_loaded_assemblies tells you which assemblies loaded and the AppDomain they’re in.

Tip: If you only see system app domains (e.g., master.sys[runtime], msdb.dbo[runtime]) and no user assemblies, the message was likely triggered by a built-in feature.


2) Map AppDomains to databases and assemblies

Run this per database where you suspect CLR usage:

-- In the target database context
SELECT a.name            AS assembly_name,
       a.permission_set_desc,
       af.file_id,       -- if present
       af.content        -- varbinary; don't SELECT in SSMS grid unless you need it
FROM sys.assemblies AS a
LEFT JOIN sys.assembly_files AS af ON a.assembly_id = af.assembly_id
ORDER BY a.is_user_defined DESC, a.name;

If you see custom assemblies here, they’re candidates for having triggered the AppDomain.


3) Check for churn in the error log (lots of create/unload)

-- Count AppDomain entries today
CREATE TABLE #elog(LogDate datetime, ProcessInfo nvarchar(50), [Text] nvarchar(max));
INSERT #elog EXEC xp_readerrorlog 0, 1, N'AppDomain';

SELECT COUNT(*) AS occurrences,
       MIN(LogDate) AS first_seen,
       MAX(LogDate) AS last_seen
FROM #elog;

-- Optional: see hourly pattern
SELECT CONVERT(char(13), LogDate, 120) AS hour_block,
       COUNT(*) AS entries
FROM #elog
GROUP BY CONVERT(char(13), LogDate, 120)
ORDER BY hour_block;

DROP TABLE #elog;

Why this matters: rapid create/unload loops can indicate AUTO_CLOSE, memory pressure, frequent assembly redeploys, or databases attaching/detaching.


4) Rule out AUTO_CLOSE (common cause of churn)

When AUTO_CLOSE is ON, closing the last connection to a database can unload its AppDomain; the next connection recreates it.\

SELECT name, is_auto_close_on
FROM sys.databases
WHERE is_auto_close_on = 1;  -- anything here should be reviewed

Fix:

ALTER DATABASE [YourDB] SET AUTO_CLOSE OFF WITH NO_WAIT;

5) Check for memory pressure leading to unloads

If memory is tight, SQL Server may reclaim CLR memory and unload domains.

-- Look at CLR memory and overall pressure signals
SELECT * FROM sys.dm_os_memory_clerks WHERE type LIKE '%CLR%';
SELECT * FROM sys.dm_os_ring_buffers WHERE ring_buffer_type = 'RING_BUFFER_RESOURCE_MONITOR';

Fix ideas

  • Set a realistic max server memory so the OS has headroom.
  • Reduce the footprint of user assemblies (cache less, dispose objects).
  • Add RAM if the box is truly constrained.

6) Look for security/permission problems (2017+)

SQL Server 2017 introduced clr strict security (on by default). Unsigned EXTERNAL_ACCESS or UNSAFE assemblies won’t load.

SELECT name, value_in_use
FROM sys.configurations
WHERE name IN ('clr strict security', 'clr enabled');

If your error log shows “assembly could not be loaded due to security policy” near the AppDomain message:

Preferred fix: sign the assembly and create a certificate/asymmetric key login with UNSAFE ASSEMBLY permission.
Last-resort (not recommended):

EXEC sp_configure 'clr strict security', 0; RECONFIGURE;   -- reduces protection

EXEC sp_configure ‘clr strict security’, 0; RECONFIGURE; — reduces protection

7) Identify what code triggered CLR

If it’s your code, it’s easy: call stack points to your CLR object. If you’re not sure:

  • Search your code base for CREATE ASSEMBLY, EXTERNAL NAME, or UDT/aggregate definitions.
  • Look for built-in features you’re using that rely on CLR in master/msdb (PBM evaluations, geography/geometry types, etc.).
  • Correlate the spid in the log (spid144 in your screenshot) with session info at the time of creation using monitoring data, default trace, or your server-side traces if you keep them.

Worked examples

Example A: First call to a user CLR function

  1. clr enabled is 1.
  2. You deploy MyUtils assembly and create dbo.RegexIsMatch in database AppDB.
  3. First user runs:
SELECT dbo.RegexIsMatch('abc', '^[a].*');
SELECT dbo.RegexIsMatch('abc', '^[a].*');
  1. Error log shows: AppDomain 7 (AppDB.dbo[runtime].2) created.
  2. You’ll see MyUtils in AppDB.sys.assemblies and in sys.dm_clr_loaded_assemblies.

Outcome: Expected. No action needed.


Example B: AppDomain thrashes all day long

  1. Error log contains hundreds of created and unloaded entries for SalesDB.dbo[runtime].
  2. Query shows SalesDB has AUTO_CLOSE = ON.
  3. After setting AUTO_CLOSE OFF, churn stops and CPU “spikes” vanish.

Outcome: Root cause was AUTO_CLOSE.


Example C: Load failures after creation

  1. You see created, followed by messages like “Assembly MyVendorTool failed verification…” or “not authorized under CLR strict security”.
  2. clr strict security = 1, assembly is EXTERNAL_ACCESS and unsigned.

Outcome: Sign the assembly and grant the certificate/asymmetric key login UNSAFE ASSEMBLY (or work with the vendor to provide a signed build). Avoid disabling strict security.


Preventive & operational tips

  • Keep AUTO_CLOSE OFF for user databases.
  • Avoid flipping clr enabled on/off repeatedly in automation.
  • For user assemblies, prefer SAFE permission set; only use EXTERNAL_ACCESS/UNSAFE when absolutely required, and sign them.
  • Monitor with a lightweight check:
-- Count AppDomain creations in the last 24h
EXEC xp_readerrorlog 0, 1, N'AppDomain', NULL, DATEADD(day,-1,GETDATE()), NULL, N'desc';

Bottom line

That “AppDomain … created” line is SQL Server telling you it spun up a CLR sandbox. One or two lines are normal. Investigate only when you see frequent create/unload cycles or follow-on errors. Use the steps above to identify the database, the assemblies involved, and the common root causes (auto-close, memory pressure, security policy), then apply the targeted fix.


Discover more from SQLyard

Subscribe to get the latest posts sent to your email.

Leave a Reply

Discover more from SQLyard

Subscribe now to keep reading and get access to the full archive.

Continue reading