{"record":{"id":"838d0500f3a60d7e","repo":"charmbracelet/crush","slug":"question-cancelled-by-user","errorCode":null,"errorMessage":"question cancelled by user","messagePattern":"question cancelled by user","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"internal/question/question.go","lineNumber":21,"sourceCode":"// the permission service pattern: publish a request over pubsub,\n// block on a channel, and resolve when the UI sends back answers.\n//\n// Only one question can be pending at a time (the tool blocks until\n// answered), so no correlation IDs are needed in the domain model.\npackage question\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/charmbracelet/crush/internal/pubsub\"\n\t\"github.com/google/uuid\"\n)\n\n// ErrCancelled is returned by Ask when the user cancels the question.\nvar ErrCancelled = errors.New(\"question cancelled by user\")\n\n// Type identifies the kind of question to present.\ntype Type string\n\nconst (\n\tTypeYesNo        Type = \"yes_no\"\n\tTypeSingleChoice Type = \"single_choice\"\n\tTypeMultiChoice  Type = \"multi_choice\"\n\tTypeFreeText     Type = \"free_text\"\n)\n\n// Choice represents a single selectable option.\ntype Choice struct {\n\tID          string `json:\"id\"`\n\tLabel       string `json:\"label\"`\n\tDescription string `json:\"description,omitempty\"`\n}\n","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/question/question.go#L3-L39","documentation":"ErrCancelled is the sentinel returned by question.Ask when the user dismisses or cancels the question instead of answering it. It lets callers distinguish deliberate cancellation from real failures.","triggerScenarios":"The user presses esc/q or otherwise aborts while a yes/no or other question prompt from Ask is displayed.","commonSituations":"A tool run pauses to ask the user for confirmation (e.g. file edit approval) and the user cancels; an agent workflow waiting on Ask receives the sentinel and must abort the pending action gracefully.","solutions":["Check errors.Is(err, question.ErrCancelled) and treat it as a benign 'user declined' outcome.","Abort the pending action without reporting an error to the user.","Optionally inform the agent/user that the action was cancelled and can be retried."],"exampleFix":"// before\nans, err := question.Ask(ctx, q)\nif err != nil { return err }\n// after\nans, err := question.Ask(ctx, q)\nif errors.Is(err, question.ErrCancelled) {\n    return errCancelledByUser // benign, no error surfaced\n}\nif err != nil { return err }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isUserCancelled(err error) bool {\n    return errors.Is(err, question.ErrCancelled)\n}","tryCatchPattern":"ans, err := question.Ask(ctx, q)\nswitch {\ncase errors.Is(err, question.ErrCancelled):\n    return nil // user declined; not an error\ncase err != nil:\n    return err\n}","preventionTips":["Always handle ErrCancelled separately from generic errors.","Never log or report user cancellation as a failure.","Design agent flows so cancellation aborts the pending action cleanly."],"tags":["ui","user-cancel","sentinel-error"],"backgroundTag":"operation-cancelled-by-user","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}