anomalyco/sst · error

ErrProtectedStage

ErrProtectedStage

Error message

cannot remove protected stage

What it means

ErrProtectedStage is thrown when an 'sst remove' is attempted against a stage marked as protected. Protection is enabled by setting the `protect` flag on the SST App (e.g. `export const app = new sst.SstConfig({ app: { ..., protect: true } })`). SST refuses the remove so production/staging stages are not accidentally destroyed.

Source

Thrown at pkg/project/stack.go:120

type StackCommandEvent struct {
	App     string
	Stage   string
	Config  string
	Command string
	Version string
}

type Error struct {
	Message string   `json:"message"`
	URN     string   `json:"urn"`
	Help    []string `json:"help"`
}

var ErrStackRunFailed = fmt.Errorf("stack run had errors")
var ErrStageNotFound = fmt.Errorf("stage not found")
var ErrPassphraseInvalid = fmt.Errorf("passphrase invalid")
var ErrProtectedStage = fmt.Errorf("cannot remove protected stage")
var ErrProtectedDevStage = fmt.Errorf("cannot run sst dev on protected stage")
var ErrPolicyViolation = fmt.Errorf("policy violations detected")
var ErrPolicyConfigError = fmt.Errorf("policy configuration error")

func (p *Project) ResolvePolicyPackPath(policyPath string) (string, error) {
	var resolvedPath string
	if filepath.IsAbs(policyPath) {
		resolvedPath = policyPath
	} else {
		resolvedPath = filepath.Join(p.PathRoot(), policyPath)
	}

	if _, err := os.Stat(resolvedPath); err != nil {
		return "", fmt.Errorf("Policy pack not found in path: %v", resolvedPath)
	}

	return resolvedPath, nil
}

View on GitHub (pinned to a0bd20f762)

Solutions

  1. If removal is truly intended, temporarily set protect: false in sst.config.ts for that stage, deploy/re-read config, run `sst remove`, then re-enable protect.
  2. Use `sst remove --stage <non-protected-stage>` instead of targeting the protected stage.
  3. Instead of removing, remove individual resources via your cloud provider console/CLI when only some resources need deletion.
  4. If protect was enabled by mistake (e.g. copied config from production), remove the `protect: true` flag from the App config.

Example fix

// before (sst.config.ts)
export const app = new sst.SstConfig({
  app: { name: "myapp", stage: "prod", protect: true },
});
// after — temporarily allow removal
export const app = new sst.SstConfig({
  app: { name: "myapp", stage: "prod", protect: false },
});
Defensive patterns

Strategy: validation

Validate before calling

import { Config } from "./sst.config";
// Before running remove, check the stage's protect flag
if (app.protect && process.argv.includes("remove")) {
  throw new Error(`Stage '${app.stage}' is protected; refusing to remove.`);
}
// Or inspect via CLI first:
// sst diff --stage <stage>  &&  grep -n 'protect' sst.config.ts

Type guard

function isProtectedStage(app: { protect?: boolean; stage: string }) {
  return app.protect === true; // narrow to a protected-stage shape before destructive ops
}

Prevention

When it happens

Trigger: Calling Project.Run (pkg/project/run.go:39) with input.Command == "remove" while p.app.Protect is true — i.e. `sst remove` or `sst remove --stage <name>` on a stage whose sst.config.ts sets protect: true.

Common situations: CI/CD cleanup jobs running `sst remove` on a prod stage that has protect enabled; developers trying to tear down a shared production stage locally; a teammate enabled protect on the stage and the old teardown script no longer works.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/964c61b3cffd2b07. Report an issue: GitHub.