The Invisible Duplicate: SitecoreAI Editing Host Deployments and --target EH

Index
TLDR;
If you set environment variables on a SitecoreAI editing host with the Sitecore CLI and omit --target EH, the variable is stored with a null target. During deployment, editing host variables get gathered twice: once for the EH target and once by the site-specific host name — and a null-target variable matches both passes. That produces a duplicate key in the generated manifest, and your editing host deployment fails.
The kicker: the duplicate is invisible in the Deploy app, the deployment error tells you nothing, and the same code deploys just fine to your other editing host, so you burn days chasing ghosts.
The fix is to delete the offending variables and re-create them with --target EH, then make sure your CI/CD pipeline always passes --target EH going forward.
The Setup
We had just converted a project to the decoupled deployments architecture. Under decoupled deployments, your authoring environment and your editing hosts deploy independently, which is great — right up until one of them starts behaving differently from the others.
Deploys are driven from an Octopus Deploy pipeline. As part of that pipeline, we set editing host environment variables through the Sitecore CLI, roughly like this:
& dotnet sitecore cloud environment variable upsert ` --environment-id $sitecoreCloudEditingHostId ` --name $key ` --value $value ` --secret $secretNotice what's not there: --target. Hold that thought.
Production deployed no problem. The staging editing host, deploying the exact same commit, failed.
The Symptom
Here is what the CLI gave us when the staging editing host deployment failed:
Starting deployment of environment 'sites'Deployment 'xxxxxxxxxxxxxxxxxxxxxx' of environment 'sites' has failedIf the problem persists, contact customer support, and provide them thesession tracing ID of 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.Please consult the CM and/or the Rendering Host logs using the CLI or theUI using the link: https://deploy.sitecorecloud.io/environment/<env-id>/logs/typesThat is the whole error. "It failed. Go read the logs." So we read the logs. The Rendering Host log showed the application starting up perfectly:
> content-sdk-nextjs-app-router@1.0.0 next:start> next start▲ Next.js 16.1.7- Local: http://localhost:3000- Network: http://xx.x.xx.xxx:3000✓ Starting...✓ Ready in 569msRegistered OpenTelemetry with service name: headapps-sitesThe app reaches next start and reports Ready, so this is not an application startup problem. Something is failing before the app ever matters — during the deployment's own manifest generation — and none of that detail is exposed in the customer facing logs.
Same code. One host deploys, the other doesn't. No actionable error. This is the worst kind of bug: the one where the tooling actively hides the thing you need to see.
Chasing Ghosts
Before this landed with a human, the AI support agent offered a tour of every plausible-but-wrong culprit:
- Check
xmcloud.build.json— make surenodeVersionis an exact version string, not a pattern. - Confirm
renderingHosts.<host>.pathpoints at the workspace root with the lock file. - Look for a missing Turborepo
start:production:...task. - Remove duplicate/conflicting editing host variables like
SITECORE_API_HOST,SITECORE_EDGE_CONTEXT_ID,GRAPH_QL_ENDPOINT, etc. — from the Variables view.
Reasonable-sounding checklist. All of it was a dead end, because the actual duplicate does not appear in the Variables view. The AI kept pattern-matching to "a duplicate you can see and delete," when the real problem was a duplicate that only exists during backend manifest generation. It cannot pattern-match its way to a root cause that lives in code it can't read.
We escalated to a human, who escalated to the product team, and that is where the real answer came from. Score one for humans; at least until they train the next model on this blog post (which is, ironically, written mostly by a robot).
The Root Cause
Here is what the product team confirmed. During deployment, an editing host's environment variables are gathered in two separate passes:
GetHostEnvironmentVariables("eh") // matches variables where Target == "EH"GetHostEnvironmentVariables(hostName) // matches variables by the site-specific host name- A variable with
Target = "EH"matches only the first pass. - A variable with
Target = nullmatches both passes.
When a variable is picked up twice, it lands in the generated YAML manifest twice — as a duplicate key. Manifest generation chokes on the duplicate, and the deployment fails with the useless generic error above.
Our pipeline created every editing host variable without --target, so they were all stored at the backend with Target = null. Every single one was a duplicate-key landmine.
Why Production Worked
This is the part that made it maddening to diagnose. Production had the same variables, but they had been created with Target = "eh". So they matched only one pass, produced one key, and deployed cleanly. Staging's variables were created with Target = null, matched both passes, and blew up.
The code was identical. The stored state of the variables was not.
| Environment | Variable Target | Manifest result | Deploy |
|---|---|---|---|
| Production | EH | one key | ✅ succeeds |
| Staging | null | duplicate key | ❌ fails |
Nothing in the Deploy app surfaces this difference. Two editing hosts that look identical in the UI behave completely differently, and the only tell is a target value you can't see without going through support.
The Fix
For us this affected five variables on the staging editing host — the Sitecore Search / AI widget set:
SITECORE_SEARCH_ENVSITECORE_SEARCH_CUSTOMER_KEYSITECORE_SEARCH_API_KEYINSIGHTS_WIDGET_IDINSIGHTS_SOURCE_IDS
The specific names don't matter. Any editing host variable with a null target is a candidate. There's no in-place "set the target" operation, so you delete each one and re-create it with --target EH.
Delete:
dotnet sitecore cloud environment variable delete \ --environment-id <editing-host-id> \ --name <variableName>Re-create with the target set:
dotnet sitecore cloud environment variable upsert \ --environment-id <editing-host-id> \ --name <variableName> \ --value <desiredValue> \ --target EH \ --secret falseQuick note on --environment-id, because it tripped us up: for an editing host you pass the editing host's own ID here, and you add --target EH. It's not the authoring environment ID with the host name as the target.
Once all the variables are re-created with --target EH, trigger a fresh deployment to the editing host. Ours went green immediately.
Fix Your Pipeline Too
Re-creating the variables by hand fixes today. It doesn't stop the next pipeline run from re-introducing null-target variables and putting you right back where you started. So add --target EH to every editing host upsert in your CI/CD.
If you're driving it from PowerShell like we are, something along these lines re-creates the whole set with the target baked in:
$editingHostId = "<editing-host-id>"
$vars = @{ "SITECORE_SEARCH_ENV" = "<value>" "SITECORE_SEARCH_CUSTOMER_KEY" = "<value>" "SITECORE_SEARCH_API_KEY" = "<value>" "INSIGHTS_WIDGET_ID" = "<value>" "INSIGHTS_SOURCE_IDS" = "<value>"}
foreach ($name in $vars.Keys) { & dotnet sitecore cloud environment variable upsert ` --environment-id $editingHostId ` --name $name ` --value $vars[$name] ` --target EH ` --secret false}The one-line takeaway for the pipeline: editing host variable upserts always get --target EH.
Takeaways
- When setting editing host variables via the Sitecore CLI, always pass
--target EH. Omitting it stores anulltarget that matches two manifest passes and yields a duplicate key. - A "same code, different host" deployment failure points at environment/config state, not at your application. The Rendering Host reaching
Readyis your clue that startup isn't the problem. - The Deploy app's Variables view does not show the target, and does not show the internal duplicate. Don't trust "the UI looks fine" as proof the state is fine.
- Generic "deployment has failed, here's a tracing ID" errors mean the real error is upstream in manifest generation. Escalate to a human early; the actionable detail lives in backend logs you can't reach.
- Audit your other editing hosts now. If one was ever populated by a pipeline that skipped
--target, it's a landmine waiting for its next deploy.
Documentation Requested
This behavior — that editing host variables need --target EH, and that omitting it can silently fail your deployment with a duplicate key — is not in the public documentation as of this writing.
If this post saved you the multi-day version of this investigation, you're welcome. Now go set your targets.
Target acquired,
-MG





