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

> A generic deploy failure, an env var with no target, and a duplicate key you can't see
Cover Image for The Invisible Duplicate: SitecoreAI Editing Host Deployments and --target EH

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 $secret

Notice 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 failed
If the problem persists, contact customer support, and provide them the
session tracing ID of 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.
Please consult the CM and/or the Rendering Host logs using the CLI or the
UI using the link: https://deploy.sitecorecloud.io/environment/<env-id>/logs/types

That 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 569ms
Registered OpenTelemetry with service name: headapps-sites

The 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 sure nodeVersion is an exact version string, not a pattern.
  • Confirm renderingHosts.<host>.path points 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 = null matches 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.

EnvironmentVariable TargetManifest resultDeploy
ProductionEHone key✅ succeeds
Stagingnullduplicate 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_ENV
  • SITECORE_SEARCH_CUSTOMER_KEY
  • SITECORE_SEARCH_API_KEY
  • INSIGHTS_WIDGET_ID
  • INSIGHTS_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 false

Quick 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 a null target 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 Ready is 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

I chose these words

More Posts

Cover Image for Hello World

Hello World

> Welcome to the show

Cover Image for Super Fast Project Builds with Visual Studio Publish

Super Fast Project Builds with Visual Studio Publish

> For when solution builds take too long

Cover Image for Managing Rich Text Editor Profiles in SitecoreAI

Managing Rich Text Editor Profiles in SitecoreAI

> And other interesting observations

Cover Image for How to Reduce the Size of Your Local SQL Databases on a Schedule

How to Reduce the Size of Your Local SQL Databases on a Schedule

> 5 minutes could save you 14% or more on disk space and car insurance