Merge pull request #1847 from rmcrackan/rmcrackan/1845-better-non-json

#1845 - improve error message for common VPN blocking errors
This commit is contained in:
rmcrackan
2026-06-02 12:58:30 -04:00
committed by GitHub
8 changed files with 114 additions and 2 deletions

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AudibleApi" Version="10.1.5.1" />
<PackageReference Include="AudibleApi" Version="10.2.0.1" />
<PackageReference Include="Google.Protobuf" Version="3.34.1" />
</ItemGroup>

View File

@@ -0,0 +1,69 @@
using AudibleApi;
using System;
using System.Collections.Generic;
namespace AudibleUtilities;
/// <summary>
/// Libation-facing helpers when Audible returns HTML instead of JSON (library scan, catalog, etc.).
/// </summary>
public static class NonJsonResponseExceptionExtensions
{
public const string LibraryScanFailedCaption = "Library scan failed";
private static readonly string[] ThingsToTryBullets =
[
"Scan again after a few minutes.",
"Sign in to Audible in a browser on the same network.",
"Disable VPN or proxy and scan again.",
"Remove and re-add the account in Libation.",
"If it still fails, check the log for Full body: near the error and attach it to a bug report.",
];
public static IEnumerable<string> GetExplainerLines(this NonJsonResponseException ex)
{
ArgumentNullException.ThrowIfNull(ex);
yield return getIntro(ex);
yield return string.Empty;
yield return "Things to try:";
foreach (var bullet in ThingsToTryBullets)
yield return "• " + bullet;
}
public static string GetExplainerBody(this NonJsonResponseException ex)
=> string.Join("\r\n", ex.GetExplainerLines());
public static bool TryFindInTree(Exception ex, out NonJsonResponseException? match)
{
ArgumentNullException.ThrowIfNull(ex);
NonJsonResponseException? found = null;
walk(ex);
match = found;
return found is not null;
void walk(Exception? e)
{
if (e is null || found is not null)
return;
if (e is NonJsonResponseException nonJson)
found = nonJson;
if (found is not null)
return;
if (e is AggregateException agg)
{
foreach (var inner in agg.InnerExceptions)
walk(inner);
}
walk(e.InnerException);
}
}
private static string getIntro(NonJsonResponseException ex)
=> ex.HtmlTitle is null
? "Audible returned an HTML page instead of the expected library data."
: $"Audible returned an HTML page ({ex.HtmlTitle}) instead of the expected library data.";
}

View File

@@ -231,6 +231,14 @@ public partial class MainVM
WebView2LoginErrorMessage.Caption,
webViewEx);
}
else if (NonJsonResponseExceptionExtensions.TryFindInTree(ex, out var htmlEx) && htmlEx is not null)
{
await MessageBox.ShowAdminAlert(
MainWindow,
htmlEx.GetExplainerBody(),
NonJsonResponseExceptionExtensions.LibraryScanFailedCaption,
htmlEx);
}
else
{
await MessageBox.ShowAdminAlert(

View File

@@ -443,6 +443,14 @@ public class ProductsDisplayViewModel : ViewModelBase
WebView2LoginErrorMessage.Caption,
webViewEx);
}
else if (NonJsonResponseExceptionExtensions.TryFindInTree(ex, out var htmlEx) && htmlEx is not null)
{
await MessageBox.ShowAdminAlert(
null,
htmlEx.GetExplainerBody(),
NonJsonResponseExceptionExtensions.LibraryScanFailedCaption,
htmlEx);
}
else
{
await MessageBox.ShowAdminAlert(

View File

@@ -1,6 +1,7 @@
using CommandLine;
using Dinah.Core;
using FileManager;
using AudibleUtilities;
using LibationFileManager;
using System;
using System.Collections.Generic;
@@ -44,6 +45,15 @@ public abstract class OptionsBase
catch (Exception ex)
{
Environment.ExitCode = (int)ExitCode.RunTimeError;
if (NonJsonResponseExceptionExtensions.TryFindInTree(ex, out var htmlEx) && htmlEx is not null)
{
foreach (var line in htmlEx.GetExplainerLines())
Console.Error.WriteLine(line);
Serilog.Log.Logger.Error(htmlEx, "Audible returned HTML instead of JSON");
return;
}
PrintVerbUsage(
"ERROR",
"=====",

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AudibleApi" Version="10.1.5.1" />
<PackageReference Include="AudibleApi" Version="10.2.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.7" />
<PackageReference Include="NameParserSharp" Version="1.5.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />

View File

@@ -96,6 +96,14 @@ public partial class Form1
WebView2LoginErrorMessage.Caption,
webViewEx);
}
else if (NonJsonResponseExceptionExtensions.TryFindInTree(ex, out var htmlEx) && htmlEx is not null)
{
MessageBoxLib.ShowAdminAlert(
this,
htmlEx.GetExplainerBody(),
NonJsonResponseExceptionExtensions.LibraryScanFailedCaption,
htmlEx);
}
else
{
MessageBoxLib.ShowAdminAlert(

View File

@@ -14,6 +14,15 @@ There are two possible causes of this error.
1. [Run hangover](#how-to-run-the-hangover-app) and execute the following command in the "Database" tab: `PRAGMA journal_mode=DELETE`
2. run this command in your terminal: `sqlite3 "path/to/libation/files/LibationContext.db" "PRAGMA journal_mode=DELETE;"`
## Library scan fails ("Unexpected character" or "HTML instead of JSON")
Audible returned an HTML page instead of JSON. Common causes: transient outage, expired login, VPN/proxy, or rate limiting. What to try:
1. Scan again after a few minutes.
2. Sign in to Audible in a browser on the same network.
3. Disable VPN/proxy and scan again.
4. Remove and re-add the account in Libation.
## How to run the Hangover App
When troubleshooting, you may be asked to run 'Hangover'. Hangover is a debugging app to help diagnose and solve some problems with Libation.