{"record":{"id":"a5fecd5606a0c6ef","repo":"docker/cli","slug":"login-canceled","errorCode":null,"errorMessage":"login canceled","messagePattern":"login canceled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"internal/oauth/manager/manager.go","lineNumber":131,"sourceCode":"\t\ttokenRes, err := m.api.WaitForDeviceToken(ctx, state)\n\t\tif err != nil {\n\t\t\twaitForTokenErrChan <- err\n\t\t\treturn\n\t\t}\n\t\ttokenResChan <- tokenRes\n\t}()\n\n\tgo func() {\n\t\treader := bufio.NewReader(os.Stdin)\n\t\t_, _ = reader.ReadString('\\n')\n\t\t_ = m.openBrowser(state.VerificationURI)\n\t}()\n\n\t_, _ = fmt.Fprint(w, \"\\nWaiting for authentication in the browser…\\n\")\n\tvar tokenRes api.TokenResponse\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn nil, errors.New(\"login canceled\")\n\tcase err := <-waitForTokenErrChan:\n\t\treturn nil, fmt.Errorf(\"failed waiting for authentication: %w\", err)\n\tcase tokenRes = <-tokenResChan:\n\t}\n\n\tclaims, err := oauth.GetClaims(tokenRes.AccessToken)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse token claims: %w\", err)\n\t}\n\n\terr = m.storeTokensInStore(tokenRes, claims.Domain.Username)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to store tokens: %w\", err)\n\t}\n\n\tpat, err := m.api.GetAutoPAT(ctx, m.audience, tokenRes)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/internal/oauth/manager/manager.go#L113-L149","documentation":"Returned by LoginDevice (internal/oauth/manager/manager.go:131) when the context passed to the method is canceled during the device-token polling loop. The select statement hits the ctx.Done() branch, indicating the user or calling code aborted the login before tokens were received. This is expected/intentional behavior, not a system failure.","triggerScenarios":"During LoginDevice's blocking select (waiting for tokenResChan, waitForTokenErrChan, or ctx.Done), the context is canceled — user presses Ctrl+C, a parent context deadline expires, or the calling code programmatically cancels the context.","commonSituations":"User presses Ctrl+C at the 'Waiting for authentication in the browser' prompt, script/CI timeout cancels the context, signal handler triggers context cancellation, or the user simply decides not to complete browser login.","solutions":["Handle this error as a normal cancellation, not a failure — check errors.Is(err, context.Canceled) and exit gracefully.","If the cancellation is unexpected, investigate what is canceling the parent context (timeout too short, signal handling).","Increase the context timeout if the user needs more time to complete browser-based authentication."],"exampleFix":"// before: treating cancellation as a hard error\nauthCfg, err := mgr.LoginDevice(ctx, os.Stdout)\nif err != nil {\n    log.Fatalf(\"login failed: %v\", err)\n}\n\n// after: distinguish cancellation from real errors\nauthCfg, err := mgr.LoginDevice(ctx, os.Stdout)\nif err != nil {\n    if errors.Is(err, context.Canceled) || err.Error() == \"login canceled\" {\n        fmt.Fprintln(os.Stderr, \"login canceled by user\")\n        os.Exit(1)\n    }\n    log.Fatalf(\"login failed: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"authCfg, err := mgr.LoginDevice(ctx, w)\nif err != nil {\n    if errors.Is(err, context.Canceled) || err.Error() == \"login canceled\" {\n        // User-initiated cancellation — exit gracefully, not an error\n        return nil\n    }\n    return err\n}","preventionTips":["Treat 'login canceled' as expected user behavior, not a system error — exit gracefully.","Use errors.Is(err, context.Canceled) to detect context-driven cancellation uniformly.","If using a timeout context, size it generously (e.g., 5+ minutes) to allow browser-based auth.","Avoid wrapping context.Canceled in other errors to keep cancellation detection clean."],"tags":["oauth","authentication","cancellation","context"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}