{"record":{"id":"95c49ee9069ef62c","repo":"Devolutions/UniGetUI","slug":"github-did-not-return-an-access-token","errorCode":null,"errorMessage":"GitHub did not return an access token.","messagePattern":"GitHub did not return an access token\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"warning","filePath":"src/UniGetUI.Interface.IpcApi/IpcBackupApi.cs","lineNumber":227,"sourceCode":"        {\n            ClearPendingGitHubDeviceFlow();\n            throw new InvalidOperationException(\n                \"The pending GitHub device flow has expired. Start sign-in again.\"\n            );\n        }\n\n        try\n        {\n            using var client = CreateAnonymousGitHubClient();\n            var token = await client.CreateAccessTokenForDeviceFlowAsync(\n                Secrets.GetGitHubClientId(),\n                pending.DeviceFlow,\n                CancellationToken.None\n            );\n\n            if (string.IsNullOrWhiteSpace(token.AccessToken))\n            {\n                throw new InvalidOperationException(\"GitHub did not return an access token.\");\n            }\n\n            SecureGHTokenManager.StoreToken(token.AccessToken);\n            using var userClient = CreateAuthenticatedGitHubClient(token.AccessToken);\n            var user = await userClient.GetCurrentUserAsync();\n            Settings.SetValue(Settings.K.GitHubUserLogin, user.Login ?? string.Empty);\n            ClearPendingGitHubDeviceFlow();\n\n            return new IpcGitHubAuthResult\n            {\n                Status = \"success\",\n                Command = \"complete-github-sign-in\",\n                Message = string.IsNullOrWhiteSpace(user.Login)\n                    ? \"GitHub sign-in completed.\"\n                    : $\"GitHub sign-in completed for {user.Login}.\",\n                Auth = await GetGitHubAuthInfoAsync(),\n            };\n        }","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/Devolutions/UniGetUI/blob/9b1d7d0eab91620fc15c86b36676532095936ae3/src/UniGetUI.Interface.IpcApi/IpcBackupApi.cs#L209-L245","documentation":"In CompleteGitHubDeviceFlowAsync, after calling CreateAccessTokenForDeviceFlowAsync the returned GitHubOAuthToken's AccessToken is checked. If it is null or whitespace, an InvalidOperationException is thrown before any token is stored. This guards against GitHub returning a 200 with an empty or missing access_token field — which happens when the device authorization has not been completed by the user yet, or when the response shape is unexpected.","triggerScenarios":"The user has not yet entered the device code in the browser, or has not approved the authorization, but the token endpoint was polled and returned a body without an access_token (e.g. an 'authorization_pending' error serialized as 200, or a malformed response).","commonSituations":"The client polls the token endpoint before the user finishes browser authorization. GitHub returns an error descriptor (e.g. {\"error\":\"authorization_pending\"}) that deserializes into a token with an empty AccessToken. A network proxy returns an empty 200. The device code was already used or expired and GitHub returns a non-token body.","solutions":["Ensure the user has completed the browser authorization before calling CompleteGitHubDeviceFlowAsync.","Poll at the device-flow interval and only complete once authorization is confirmed.","If this persists, restart the device flow (StartGitHubDeviceFlowAsync) and have the user re-authorize."],"exampleFix":"// before: completing before the user finishes authorization\nawait IpcBackupApi.CompleteGitHubDeviceFlowAsync();\n// after: poll until the user has authorized, then complete\nvar auth = await IpcBackupApi.GetStatusAsync();\nif (auth.Auth.IsAuthenticated || /* user confirmed browser approval */ true)\n    await IpcBackupApi.CompleteGitHubDeviceFlowAsync();","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await IpcBackupApi.CompleteGitHubDeviceFlowAsync(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"did not return an access token\"))\n{ /* user has not finished browser auth yet; poll again or restart flow */ }","preventionTips":["Only call CompleteGitHubDeviceFlowAsync after the user confirms browser authorization.","Poll at the device-flow interval to avoid premature token requests.","Restart the device flow if authorization cannot be completed in time."],"tags":["github","oauth","device-flow","authentication","token","ipc"],"backgroundTag":null,"analyzedSha":"9b1d7d0eab91620fc15c86b36676532095936ae3","analyzedAt":"2026-08-13T12:19:18.278Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}