{"record":{"id":"0448f3fdae487320","repo":"hashicorp/terraform","slug":"operation-canceled","errorCode":null,"errorMessage":"operation canceled","messagePattern":"operation canceled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/command/meta.go","lineNumber":530,"sourceCode":"\t\t// Notify the user\n\t\topReq.View.Interrupted()\n\n\t\t// Still get the result, since there is still one\n\t\tselect {\n\t\tcase <-m.ShutdownCh:\n\t\t\topReq.View.FatalInterrupt()\n\n\t\t\t// cancel the operation completely\n\t\t\top.Cancel()\n\n\t\t\t// the operation should return asap\n\t\t\t// but timeout just in case\n\t\t\tselect {\n\t\t\tcase <-op.Done():\n\t\t\tcase <-time.After(5 * time.Second):\n\t\t\t}\n\n\t\t\treturn nil, errors.New(\"operation canceled\")\n\n\t\tcase <-op.Done():\n\t\t\t// operation completed after Stop\n\t\t}\n\tcase <-op.Done():\n\t\t// operation completed normally\n\t}\n\n\treturn op, nil\n}\n\n// contextOpts returns the options to use to initialize a Terraform\n// context with the settings from this Meta.\nfunc (m *Meta) contextOpts() (*terraform.ContextOpts, error) {\n\tworkspace, err := m.Workspace()\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":512,"sourceCodeEnd":548,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/command/meta.go#L512-L548","documentation":"Returned by Meta.RunOperation (meta.go:493) when a SECOND interrupt is received. The first signal on m.ShutdownCh triggers a graceful op.Stop(); if the operator hits Ctrl-C again while waiting (line 517 branch), the code calls op.Cancel() for a hard cancel, waits up to 5s, and returns errors.New(\"operation canceled\"). It signals the operation was forcibly terminated rather than completing.","triggerScenarios":"Pressing Ctrl-C twice (or sending SIGINT twice) during a long-running `terraform apply`, `terraform plan`, `terraform destroy`, or `terraform refresh` — the first initiates graceful shutdown, the second forces cancellation.","commonSituations":"Operator impatient during a slow apply and mashing Ctrl-C; CI runner sending SIGINT twice on timeout/job cancel; a hung provider that ignores graceful Stop, prompting a second interrupt.","solutions":["Press Ctrl-C once and wait for graceful shutdown; only interrupt again if the operation is truly hung.","If a provider routinely ignores Stop, upgrade the provider plugin or check for stuck external API calls.","For automation, send a single SIGINT and poll the process exit rather than double-signaling."],"exampleFix":"# before: double Ctrl-C -> hard cancel, partial state possible\n ^C^C\n# after: single interrupt, let graceful Stop complete\n ^C   # wait for 'Interrupt received' then clean exit","handlingStrategy":"try-catch","validationCode":"// No pre-validation prevents a double-SIGINT; instead structure signal handling.\n sigCh := make(chan os.Signal, 1)\n signal.Notify(sigCh, os.Interrupt)","typeGuard":null,"tryCatchPattern":"op, err := meta.RunOperation(b, opReq)\n if err != nil && err.Error() == \"operation canceled\" {\n     // double-interrupt hard-cancel; surface partial-state risk to the operator\n     log.Println(\"operation forcibly cancelled; verify state consistency\")\n }","preventionTips":["Send Ctrl-C once and wait for graceful shutdown; double-interrupt forces cancel and can leave partial state.","In automation, send a single SIGINT and poll for process exit rather than re-signaling."],"tags":["interrupt","signal-handling","apply","shutdown"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}