Tyrrrz/DiscordChatExporter · error · DiscordChatExporterException

Request to '{url}' failed: {response.StatusCode.ToString().S

Error message

Request to '{url}' failed: {response.StatusCode.ToString().SeparateWords(' ').ToLowerInvariant()}.
Response content: {await response.Content.ReadAsStringAsync(cancellationToken)}

What it means

Thrown as DiscordChatExporterException(isFatal=true) from the default arm of GetJsonResponseAsync's status switch for any non-success status not handled by 401/403/404 (e.g. 429 Too Many Requests, 5xx server errors, 400 Bad Request). The message embeds both the humanized status (words separated and lower-cased) and the raw response body to aid diagnosis. isFatal is set because these usually indicate systemic or server-side conditions.

Source

Thrown at DiscordChatExporter.Core/Discord/DiscordClient.cs:158

        if (!response.IsSuccessStatusCode)
        {
            throw response.StatusCode switch
            {
                HttpStatusCode.Unauthorized => throw new DiscordChatExporterException(
                    "Authentication token is invalid.",
                    true
                ),

                HttpStatusCode.Forbidden => throw new DiscordChatExporterException(
                    $"Request to '{url}' failed: forbidden."
                ),

                HttpStatusCode.NotFound => throw new DiscordChatExporterException(
                    $"Request to '{url}' failed: not found."
                ),

                _ => throw new DiscordChatExporterException(
                    $"""
                    Request to '{url}' failed: {response
                        .StatusCode.ToString()
                        .SeparateWords(' ')
                        .ToLowerInvariant()}.
                    Response content: {await response.Content.ReadAsStringAsync(
                        cancellationToken
                    )}
                    """,
                    true
                ),
            };
        }

        return await response.Content.ReadAsJsonAsync(cancellationToken);
    }

    private async ValueTask<JsonElement?> TryGetJsonResponseAsync(

View on GitHub (pinned to f6865c8216)

Solutions

  1. For 429: reduce --parallel, slow down, and respect Retry-After; consider running exports sequentially.
  2. For 5xx: check discordstatus.com and retry after a delay; these are typically transient.
  3. For 4xx other than 401/403/404: read the Response content in the message — it usually names the invalid parameter.
  4. Upgrade DiscordChatExporter — an unexpected status can mean Discord changed an endpoint the tool targets.

Example fix

// before
dotnet run -- exportall -g <id> -t <token> -p 10   # 10-way parallel triggers 429
// after
dotnet run -- exportall -g <id> -t <token> -p 1        # serialize to respect rate limits
Defensive patterns

Strategy: retry

Validate before calling

// Cannot prevent server-side 5xx/429, but reduce likelihood:
// keep --parallel low and back off on prior failures.
if (options.Parallel > 3) options.Parallel = 3; // conservative default

Try / catch

try { await exporter.ExportChannelAsync(req, progress, ct); }
catch (DiscordChatExporterException ex) when (ex.IsFatal)
{
    if (IsRateLimited(ex)) await Task.Delay(GetRetryAfter(ex), ct); // then retry once
    else throw;
}

Prevention

When it happens

Trigger: Any GetJsonResponseAsync call whose response status is not 200/401/403/404. The default switch arm at DiscordClient.cs:158 builds the message from `response.StatusCode.ToString().SeparateWords(' ').ToLowerInvariant()` plus `response.Content.ReadAsStringAsync`.

Common situations: Hitting Discord's global/cloudflare rate limit (429) after aggressive parallelism; Discord 5xx during an outage; malformed request triggering 400; deprecated endpoint returning an unexpected status after an API change; IP banned/temporarily blocked.

Related errors


AI-assisted analysis of Tyrrrz/DiscordChatExporter@f6865c8216 (2026-08-13). Data as JSON: /api/errors/5510437c0be7dde7. Report an issue: GitHub.