Runbook: Release Orchestrator - Rollback Operation Failed
For platform and release on-call during an incident. Use this when a Stella Ops deployment rollback will not start, returns a non-2xx, hangs in rolling_back, or lands in failed with a rollback_failed event. It explains how rollback actually works (a state transition on an existing deployment job, driven through the HTTP API — there is no live CLI path today) and how to recover safely.
Sprint: SPRINT_20260117_029_DOCS_runbook_coverage Task: RUN-004 - Release Orchestrator Runbooks Reconciled: 2026-05-31 against
src/ReleaseOrchestrator(deployment + v2 run/environment endpoints,LibraryDeploymentRequestStarter/WebApiDeploymentRollbackStarter),src/Doctor/__Plugins/StellaOps.Doctor.Plugin.Release, the scope catalogStellaOpsScopes.cs, and the live CLI root (Cli/StellaOps.Cli/Commands/CommandFactory.cs,Program.cs).
Metadata
| Field | Value |
|---|---|
| Component | Release Orchestrator (src/ReleaseOrchestrator) |
| Severity | Critical |
| On-call scope | Platform team, Release team |
| Last updated | 2026-05-31 |
| Doctor check | check.release.rollback.readiness (plugin stellaops.doctor.release) |
How rollback works (ground truth)
A rollback is a state transition on an existing deployment job, not a standalone resource. The operation is exposed over HTTP at:
POST /api/v1/release-orchestrator/deployments/{id}/rollback
POST /api/release-orchestrator/deployments/{id}/rollback (unversioned alias)
Body (optional): { "reason": "<free text for the audit trail>" }
(Source: __Apps/StellaOps.ReleaseOrchestrator.WebApi/Endpoints/DeploymentEndpoints.cs.)
Key facts to keep in mind while diagnosing:
- Required scope: the rollback endpoint is gated by
ReleaseApprove, which maps to the canonical scoperelease:publish(NOTrelease:write). A 403 here means the caller lacksrelease:publish. (Source:ReleaseOrchestratorPolicies.cs; scope catalogStellaOpsScopes.cs->ReleasePublish = "release:publish".) - Tenant required: every deployment route calls
RequireTenant(). A missing/!mismatched tenant fails before any rollback logic runs. - Allowed source states: rollback is only accepted when the deployment is in one of
completed,succeeded,failed,running, orpaused. From any other state the store returns409 Conflict(success: false). On accept, the deployment transitions torolling_backand emits arollback_startedevent. - Real vs. compat path: when the real deploy engine is enabled (
DeployEngineSwitch.UseRealEngine) and{id}parses as a GUID, the request is handed toWebApiDeploymentRollbackStarter, which drivesIDeploymentEnginePipeline.ExecuteRollbackAsync. If no pipeline is registered, or the job is not found, it falls back to a plain compat-store state transition. (Source:Services/LibraryDeploymentRequestStarter.cs.) - Terminal states / events: on success the job reaches
RolledBack-> compat statusrolled_back, eventrollback_completed, and (whenIReleaseTruthRepository+RollbackEvidenceGeneratorare present) a rollback evidence record is persisted. On failure the status isfailed, eventrollback_failed, carrying the pipelineFailureReason. (Source:WebApiDeploymentRollbackStarter.RollbackAsyncinServices/LibraryDeploymentRequestStarter.cs.) - A second, demo-only rollback route exists:
POST /api/v1/runs/{id}/rollback(alsoPOST /v1/runs/{id}/rollback), gated by the sameReleaseApprove/release:publishpolicy andRequireTenant(). It is the promotion-run rollback contract, not the deployment-job rollback. As of this reconciliationReleaseControlV2Endpoints.TriggerRollbackis backed by a hardcodedRunCatalog(onlyrun-001), accepts a rollback only when the run status isfailed/warning/degraded(otherwise400 rollback_guard_blocked), and returns a canned202 Acceptedplan — it does not execute a real rollback. Use the deployment-job rollback (/deployments/{id}/rollback) above for actual recovery. (Source:ReleaseControlV2Endpoints.MapRunsV2/TriggerRollback.)
NOTE (CLI surface — no live rollback command): the live
stellaCLI root is built byCommandFactory.Create(the entrypoint invoked fromCli/StellaOps.Cli/Program.cs), and it does not register anyrelease rollback/release deploycommand group. Therelease ... rollback/release deploy run|status|historyhandlers inCli/StellaOps.Cli/Commands/ReleaseCommandGroup.cs(BuildReleaseCommand) print hardcoded sample output and — equally important —BuildReleaseCommandis never added to the root, so those commands are unreachable dead code. The parallelCli/StellaOps.Cli/CliApplication.cstree (withdeploy rollback/deploy status/deploy logsagainst/api/v1/deployments, backed by aDeployCommandHandlerwhose methods returnTask.CompletedTask) is likewise never instantiated as the live root and is also dead code. There is therefore no CLI path to roll back a deployment today — drive rollbacks through the HTTP API below. (Verified againstCommandFactory.Create’s root registration list andProgram.cs.)
Symptoms
- [ ] Rollback POST returns non-2xx, or the deployment is stuck in
rolling_back - [ ] Deployment reaches
failedafter rollback with eventrollback_failed - [ ]
409 Conflicton rollback: deployment is not in an allowed source state - [ ]
403 Forbiddenon rollback: caller is missing therelease:publishscope - [ ] Doctor reports
check.release.rollback.readinessasfail/warn - [ ] Target environment in an inconsistent state after a partial rollback
Impact
| Impact Type | Description |
|---|---|
| User-facing | Rollback blocked; potentially broken release in production |
| Data integrity | Environment may be in partial rollback state (rolling_back not converging) |
| SLA impact | Incident resolution blocked; extended outage |
Diagnosis
The examples below use
curlagainst the orchestrator HTTP API because the live CLI has no rollback/deploy command (see the CLI NOTE above). Send a bearer token with therelease:readscope for reads andrelease:publishfor the rollback itself, plus theX-Tenant-Idyour gateway expects.
Quick checks
Check Doctor diagnostics (rollback readiness):
stella doctor --check check.release.rollback.readinessThis check probes the orchestrator for production environments whose rollback is blocked or that lack a previous version / health probe. (Source:
RollbackReadinessCheck.cs.)Inspect the deployment job state:
curl -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: $TENANT" \ "$ORCH/api/v1/release-orchestrator/deployments/$DEPLOYMENT_ID"Look at
status(rolling_back,rolled_back,failed, …),progress, andcanRollback.Check deployment events (rollback_started / rollback_failed):
curl -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: $TENANT" \ "$ORCH/api/v1/release-orchestrator/deployments/$DEPLOYMENT_ID/events"Review per-environment deployment history (previous versions available to roll back to):
curl -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: $TENANT" \ "$ORCH/api/v1/environments/$ENV_ID/deployments"(Source:
ReleaseControlV2Endpoints.MapEnvironmentsV2->GET /api/v1/environments/{id}/deployments.)CAVEAT: this v2 route is not yet backed by live data —
GetEnvironmentDeploymentsserves a hardcoded in-memoryEnvironmentCatalog(only the demoenv-production/env-stagingentries), so it returns fixture deployment history rather than the calling tenant’s real deployments. For authoritative per-deployment state use the deployment detail/events endpoints above; treat this route as illustrative until it is wired to the deployment store.
Deep diagnosis
Read the failure reason from logs and events. The
rollback_failedevent message carries the pipelineFailureReason. Pull logs for the deployment:curl -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: $TENANT" \ "$ORCH/api/v1/release-orchestrator/deployments/$DEPLOYMENT_ID/logs?level=error"Confirm a previous release exists to roll back to. A
noPreviousVersion/canRollback=falsecondition is the most common blocker. Per the readiness check, the recognised causes are: previous deployment artifacts not retained, a non-reversible database migration, a breaking change deployed, or rollback manually disabled. (Source:RollbackReadinessCheck.WithCauses(...).)Check the engine path. If the deployment ID is a GUID and the real engine is enabled, the rollback runs through
IDeploymentEnginePipeline. IfRollbackAsynclogged “no IDeploymentEnginePipeline registered” or “job not found”, the request silently fell back to a compat-store transition and no real recovery executed - check the orchestrator service logs for those warnings. (Source:WebApiDeploymentRollbackStarter.)Verify scope and tenant if the POST is rejected: 403 -> missing
release:publish; tenant errors -> the route’sRequireTenant()filter rejected the request before reaching rollback logic.
Resolution
Immediate mitigation
Re-issue the rollback with an audit reason (idempotent if the job is in an allowed state):
curl -X POST -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: $TENANT" \ -H "Content-Type: application/json" \ -d '{"reason":"<incident-id>: roll back failed release"}' \ "$ORCH/api/v1/release-orchestrator/deployments/$DEPLOYMENT_ID/rollback"A
409response means the deployment is not incompleted/succeeded/failed/running/paused; resolve the current state first (e.g. cancel a stuck run) before retrying.If a run is stuck before you can roll back, cancel it, then redeploy the last known-good release as a fresh deployment:
# cancel the in-flight deployment (requires release:write) curl -X POST -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: $TENANT" \ "$ORCH/api/v1/release-orchestrator/deployments/$DEPLOYMENT_ID/cancel" # create a new deployment pinning the previous (known-good) release curl -X POST -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: $TENANT" \ -H "Content-Type: application/json" \ -d '{"releaseId":"<previous-release-id>","environmentId":"<env-id>","strategy":"rolling"}' \ "$ORCH/api/v1/release-orchestrator/deployments"(Source:
DeploymentEndpoints.CreateAsync;strategymust be one ofrolling,canary,blue_green,all_at_once.)
Root cause fix
If no previous version is available (canRollback=false, noPreviousVersion):
The readiness check’s manual remediation is to configure artifact retention so prior releases remain rollback-eligible. The check surfaces this exact step (step 3,
CommandType.Manual):stella config set Release:ArtifactRetention:Count 5(Source:
RollbackReadinessCheck.WithRemediationstep 3.) NB:stella config setis not a live CLI command — the livestella configgroup exposes onlyshow/list/notify/integrations/feeds/registry/sources, with no genericset. Treat the surfaced command as descriptive of the intended config key; setRelease:ArtifactRetention:Countthrough the orchestrator’s configuration (env/appsettings/DB overrides) rather than this command. (There is likewise nostella registry retention/restorecommand.)If the previous artifact was pruned, redeploy from a build that is still present in the registry. NB: the
stella registry list/stella registry tagscommands defined inCli/StellaOps.Cli/Commands/RegistryCommandGroup.cs(BuildRegistryCommand) are not registered in the live CLI root (CommandFactory.Createonly wiresstella config registryfor registry configuration), so there is currently no live CLI to enumerate registry contents — inspect the registry directly or through its own API/UI.
If the deploy engine pipeline is not executing the rollback:
Confirm
DeployEngineSwitch.UseRealEngineis enabled and anIDeploymentEnginePipelineis registered in the orchestrator. If the service logs the “no IDeploymentEnginePipeline registered” warning, the rollback only flips compat-store status and performs no real recovery.Ensure the deployment ID is the canonical GUID; non-GUID IDs bypass the real engine entirely.
If a production environment is missing a health probe (noHealthProbe warning):
- The readiness check recommends configuring a health probe and enabling auto-rollback-on-failure for the environment (surfaced as manual steps; there is no live
stella env configurecommand yet). Track environment readiness viacheck.release.environment.readinessandcheck.environment.deployments.
Verification
# Verify the deployment reached a terminal rollback state
curl -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: $TENANT" \
"$ORCH/api/v1/release-orchestrator/deployments/$DEPLOYMENT_ID"
# Expect status = "rolled_back" and a rollback_completed event
# Re-run the Doctor readiness check
stella doctor --check check.release.rollback.readiness
Prevention
- [ ] Retention: keep enough prior releases that production can always roll back (
Release:ArtifactRetention:Count); the readiness check’s remediation suggests5as a starting value (it is a recommendation in the remediation step, not a threshold the check itself enforces). - [ ] Health probes: configure a health probe per production environment so auto-rollback can trigger and
check.release.rollback.readinessstays green. - [ ] Engine wiring: keep
DeployEngineSwitch.UseRealEngineon with anIDeploymentEnginePipelineregistered so rollbacks perform real recovery, not just a status flip. - [ ] Scope hygiene: ensure the on-call rollback operator/service account holds
release:publish(not onlyrelease:write). - [ ] Testing: exercise rollback in staging regularly; assert
rolled_backstatus and the persisted rollback evidence record.
Related Resources
- Doctor article:
docs/doctor/articles/release/rollback-readiness.md(runbook referenced by the readiness check). - Module docs:
docs/modules/release-orchestrator/- seeenhancements/rollback-intelligence.mdfor the rollback-intelligence (anomaly detection / baseline / predictive) subsystem under__Libraries/StellaOps.ReleaseOrchestrator.Deployment/Rollback/. - Related runbooks:
orchestrator-promotion-stuck.md,orchestrator-gate-timeout.md,orchestrator-evidence-missing.md.
Reconciliation note: the original draft referenced
docs/modules/release-jobengine/rollback.mdanddocs/operations/rollback-procedures.md; neither exists in this docs tree. Links above point at the documents that do exist.
