{"record":{"id":"6b211072a7e5e722","repo":"nocobase/nocobase","slug":"refusing-to-destroy-current-env-envname-witho","errorCode":null,"errorMessage":"Refusing to destroy current env \"${envName}\" without explicit selection in non-interactive mode.\nRe-run with `--env ${envName} --force` to destroy this env.","messagePattern":"Refusing to destroy current env \"(.+?)\" without explicit selection in non-interactive mode\\.\nRe-run with `--env (.+?) --force` to destroy this env\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/cli/src/commands/app/destroy.ts","lineNumber":101,"sourceCode":"    lines.push('The saved CLI env config will be removed.');\n  }\n\n  lines.push(`Type \"${runtime.envName}\" to confirm:`);\n  return lines.join('\\n');\n}\n\nasync function confirmDestroy(\n  runtime: ManagedAppRuntime,\n  options: {\n    explicitEnv: boolean;\n    force: boolean;\n    removesManagedLocalAppFiles: boolean;\n    removesStorageData: boolean;\n  },\n): Promise<boolean> {\n  if (!isInteractiveTerminal()) {\n    if (!options.explicitEnv) {\n      throw new Error(formatDestroyForceRequiredMessage(runtime.envName, false));\n    }\n    if (options.force) {\n      return true;\n    }\n    throw new Error(formatDestroyForceRequiredMessage(runtime.envName, options.explicitEnv));\n  }\n\n  if (options.force) {\n    return true;\n  }\n\n  try {\n    await input({\n      message: buildDestroyPrompt(runtime, options),\n      required: true,\n      validate: (value) => (value.trim() === runtime.envName ? true : `Type \"${runtime.envName}\" to confirm.`),\n      placeholder: runtime.envName,\n    });","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/nocobase/nocobase/blob/fa42722fefe44265490dff2c27d79e2882bce4fa/packages/core/cli/src/commands/app/destroy.ts#L83-L119","documentation":"In `app destroy`, confirmDestroy refuses to proceed in a non-interactive terminal when the current env was not explicitly selected with --env (explicitEnv is false), because destroy would otherwise target an unintended default env. It throws formatDestroyForceRequiredMessage(envName, false): 'Refusing to destroy current env \"<name>\" without explicit selection in non-interactive mode. Re-run with `--env <name> --force`'.","triggerScenarios":"Piping/CI execution (stdin not a TTY) of `nocobase app destroy` without --env, relying on the current/default env, and without --force; the guard also fires when --env is given but --force is missing (second branch with explicitEnv=true).","commonSituations":"CI pipelines or scripts calling destroy non-interactively against the default env; shell scripts piped through ssh where stdin is not a terminal; forgetting --force after adding --env.","solutions":["Re-run with explicit selection and confirmation: `nocobase app destroy --env <name> --force`.","If interactive destruction is intended, run it in a TTY without flags and answer the prompt.","In CI, parametrize the env name and add --force only after verifying the target env.","Verify which env is 'current' first (`nocobase app list`) to avoid destroying the wrong one."],"exampleFix":"// before (CI, non-interactive)\nnocobase app destroy\n// after\nnocobase app destroy --env staging --force","handlingStrategy":"validation","validationCode":"import { isInteractiveTerminal } from './confirm';\nconst args = process.argv.slice(2);\nconst isNonInteractive = !isInteractiveTerminal();\nif (isNonInteractive && args.includes('destroy') && (!args.includes('--env') || !args.includes('--force'))) {\n  throw new Error('Non-interactive destroy requires both --env <name> and --force');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await appDestroy(options);\n} catch (error) {\n  if ((error as Error).message.includes('Refusing to destroy')) {\n    console.error(`Add explicit selection and confirmation: nocobase app destroy --env ${envName} --force`);\n  } else throw error;\n}","preventionTips":["Always pass --env explicitly in scripts and CI; never rely on the current/default env.","Gate --force behind a CI variable/manual approval step since destroy is irreversible.","Run destroy commands in an interactive TTY when exploring, so you get the confirmation prompt.","Run `nocobase app list` first to confirm which env is 'current' before any destroy."],"tags":["cli","destroy","non-interactive","safety-guard"],"backgroundTag":"destructive-command-needs-confirmation","analyzedSha":"fa42722fefe44265490dff2c27d79e2882bce4fa","analyzedAt":"2026-09-01T00:54:31.202Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}