{"record":{"id":"66e687ce48721ce1","repo":"Tyrrrz/DiscordChatExporter","slug":"authentication-token-is-invalid","errorCode":null,"errorMessage":"Authentication token is invalid.","messagePattern":"Authentication token is invalid\\.","errorType":"exception","errorClass":"DiscordChatExporterException","httpStatus":401,"severity":"critical","filePath":"DiscordChatExporter.Core/Discord/DiscordClient.cs","lineNumber":121,"sourceCode":"            \"users/@me\",\n            TokenKind.User,\n            cancellationToken\n        );\n\n        if (userResponse.StatusCode != HttpStatusCode.Unauthorized)\n            return (_resolvedTokenKind = TokenKind.User).Value;\n\n        // Try authenticating as a bot\n        using var botResponse = await GetResponseAsync(\n            \"users/@me\",\n            TokenKind.Bot,\n            cancellationToken\n        );\n\n        if (botResponse.StatusCode != HttpStatusCode.Unauthorized)\n            return (_resolvedTokenKind = TokenKind.Bot).Value;\n\n        throw new DiscordChatExporterException(\"Authentication token is invalid.\", true);\n    }\n\n    private async ValueTask<HttpResponseMessage> GetResponseAsync(\n        string url,\n        CancellationToken cancellationToken = default\n    ) =>\n        await GetResponseAsync(\n            url,\n            await ResolveTokenKindAsync(cancellationToken),\n            cancellationToken\n        );\n\n    private async ValueTask<JsonElement> GetJsonResponseAsync(\n        string url,\n        CancellationToken cancellationToken = default\n    )\n    {\n        using var response = await GetResponseAsync(url, cancellationToken);","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/Tyrrrz/DiscordChatExporter/blob/f6865c82167c3bfee4455da80a1f1ca5e71b95ad/DiscordChatExporter.Core/Discord/DiscordClient.cs#L103-L139","documentation":"Thrown as DiscordChatExporterException(isFatal=true) inside ResolveTokenKindAsync after probing both `users/@me` as a user token AND as a bot token and receiving HttpStatusCode.Unauthorized for both. Because neither authentication path succeeded, the token is definitively invalid. The isFatal flag tells the CLI runner this is not a transient/per-channel issue.","triggerScenarios":"ResolveTokenKindAsync is called (triggered by any API call). The user-token probe at users/@me returned non-401 so it tried bot; the bot probe at DiscordClient.cs:121 also returned 401; both failed so the exception is raised.","commonSituations":"Token typo or truncation; token copied with stray whitespace/quotes; token revoked by Discord (password change, logout, bot reset); using a bot token in a user-token field or vice-versa; environment variable DISCORD_TOKEN unset/empty resolving to garbage.","solutions":["Re-issue the token: regenerate a bot token in the Discord Developer Portal, or capture a fresh user token from the client.","Pass the token explicitly with -t to rule out env-var corruption: `-t \"<exact token>\"`.","Trim surrounding whitespace/quotes from the token before passing it.","Confirm the token kind matches intent (bot token for self-bots is unsupported; user tokens are against Discord ToS and may be invalidated)."],"exampleFix":"// before\nexportchat -c <id> -t \"$DISCORD_TOKEN\"  # env had a stale token\n// after\nexportchat -c <id> -t \"<freshly copied token>\"","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(token) || token.Trim().Length < 10)\n    throw new InvalidOperationException(\"Token appears missing or malformed.\");\n// optional: probe before the batch\nvar probe = await discord.TryGetUserAsync(currentUser, ct);\nif (probe is null) throw new InvalidOperationException(\"Token rejected by Discord.\");","typeGuard":null,"tryCatchPattern":"try { await discord.GetGuildAsync(guildId, ct); }\ncatch (DiscordChatExporterException ex) when (ex.IsFatal && ex.Message.Contains(\"invalid\"))\n{\n    logger.LogError(\"Token invalid or expired — refresh and retry\");\n    throw;\n}","preventionTips":["Store tokens in a secret manager; never hardcode.","Probe the token (e.g. users/@me) before starting a long batch.","Trim whitespace and strip quotes when reading tokens from env/files."],"tags":["authentication","token","discord-api","fatal"],"backgroundTag":null,"analyzedSha":"f6865c82167c3bfee4455da80a1f1ca5e71b95ad","analyzedAt":"2026-08-13T18:08:46.403Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}