[PR #4885] Implement experimental DNS pre-resolution, enabling Podcast downloads even with partial DNS resolution failures #4365

Open
opened 2026-04-25 00:19:26 +02:00 by adam · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/advplyr/audiobookshelf/pull/4885
Author: @PassionateBytes
Created: 12/2/2025
Status: 🔄 Open

Base: masterHead: fix-dns-resolution


📝 Commits (3)

  • da040c7 Implement experimental DNS pre-resolution
  • 33d2839 Use experimental DNS resolution on redirects
  • fc81be0 Handle redirect loops and maximum redirect limits

📊 Changes

1 file changed (+78 additions, -0 deletions)

View changed files

📝 server/Server.js (+78 -0)

📄 Description

Brief summary

This PR introduces an optional configuration parameter EXP_DNS_RESOLUTION to the server, which enables a custom mechanism for pre-resolving hostnames in all axios web requests. This mechanism uses the dedicated dns package to explicitly resolve A and AAAA records. This bypasses the getaddrinfo syscall, which in the Alpine container environment fails entirely, if only part of the DNS query results in a SERVFAIL.

This enables successful web requests in situations that would otherwise fail due to partial DNS resolution failures. - i.e. situations in which a DNS query resolves an A record of a hostname, while rejecting requests for AAAA records (R-Code SERVFAIL, or REFUSED). I have found that this causes the getaddrinfo syscall to fail entirely inside the Alpine container environment specifically, with the result that the entire web request fails. (Note that this is an alpine specific problem).

Enabling this new feature with EXP_DNS_RESOLUTION=1 will trigger explicit DNS resolution, with graceful fallback between IPv4 and IPv6. If either fails to resolve, while the other succeeds, the web request will still be allowed to go through, rather than failing entirely.

Which issue is fixed?

Additionally, a discussion of the issue server side can be found here:

...potentially more than just these issues - I have seen the same problem manifest for multiple different podcasts myself, so I expect to find more related issues. Will add them here when I come across more.

In-depth Description

It appears that in some environments axios' fails to execute a web request entirely, when a domain name gets resolved only partialy.
This can be the case when a DNS server is misconfigured, or overly restrictive, such that it only resolves IPv4 addresses (A records) successfully, while rejecting IPv6 address requests (AAAA records) with SERVFAIL (or vise versa).

Consider the example below:

Understanding Status Quo:

As an example, the German News Podcast 'Lage der Nation' hosts their public feeds on feeds.lagedernation.org and their episodes on cdn.lagedernation.org.
Resolving these domains returns IPv4 addresses (A record) successfully. However, their DNS server configuration appears to be faulty, because it consistently leads to a SERVFAIL when attempting to resolve for IPv6 addresses (AAAA records).

This failure mode can be replicated in various environments:

$ nslookup feeds.lagedernation.org
Server:		127.0.0.53
Address:	127.0.0.53#53

Non-authoritative answer:
Name:	feeds.lagedernation.org
Address: 139.162.176.53
;; Got SERVFAIL reply from 127.0.0.53
** server can't find feeds.lagedernation.org: SERVFAIL

In certain environments, axios appears to fail the entire web request when it encounters such a partial DNS resolution failure, even though a valid IPv4 address was returned. This results in errors like:

Full Error Message...

[2025-12-02 19:49:57.542] ERROR: [podcastUtils] getPodcastFeed Error AxiosError: getaddrinfo EAI_AGAIN feeds.lagedernation.org
    at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26) {
  hostname: 'feeds.lagedernation.org',
  syscall: 'getaddrinfo',
  code: 'EAI_AGAIN',
  errno: -3001,
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [Function: httpAdapter],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 30000,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function] },
    validateStatus: [Function: validateStatus],
    headers: {
      Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8',
      'Accept-Encoding': 'gzip, compress, deflate',
      'User-Agent': 'audiobookshelf (+https://audiobookshelf.org; like iTMS)'
    },
    url: 'https://feeds.lagedernation.org/feeds/ldn-mp3.xml',
    method: 'get',
    responseType: 'arraybuffer',
    httpAgent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype] {},
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 0,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      createConnection: [Function (anonymous)],
      [Symbol(shapeMode)]: false,
      [Symbol(kCapture)]: false,
      [Symbol(active)]: true
    },
    httpsAgent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype],
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      createConnection: [Function (anonymous)],
      [Symbol(shapeMode)]: false,
      [Symbol(kCapture)]: false,
      [Symbol(active)]: true
    },
    data: undefined
  },
  request: <ref *1> Writable {
    _events: {
      close: undefined,
      error: [Function: handleRequestError],
      prefinish: undefined,
      finish: undefined,
      drain: undefined,
      response: [Function: handleResponse],
      socket: [Array],
      timeout: undefined,
      abort: undefined
    },
    _writableState: WritableState {
      highWaterMark: 16384,
      length: 0,
      corked: 0,
      onwrite: [Function: bound onwrite],
      writelen: 0,
      bufferedIndex: 0,
      pendingcb: 0,
      [Symbol(kState)]: 17580812,
      [Symbol(kBufferedValue)]: null
    },
    _maxListeners: undefined,
    _options: {
      maxRedirects: 21,
      maxBodyLength: 10485760,
      protocol: 'https:',
      path: '/feeds/ldn-mp3.xml',
      method: 'GET',
      headers: [Object],
      agent: [Agent],
      agents: [Object],
      auth: undefined,
      hostname: 'feeds.lagedernation.org',
      port: null,
      nativeProtocols: [Object],
      pathname: '/feeds/ldn-mp3.xml'
    },
    _ended: true,
    _ending: true,
    _redirectCount: 0,
    _redirects: [],
    _requestBodyLength: 0,
    _requestBodyBuffers: [],
    _eventsCount: 3,
    _onNativeResponse: [Function (anonymous)],
    _currentRequest: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [TLSSocket],
      _header: 'GET /feeds/ldn-mp3.xml HTTP/1.1\r\n' +
        'Accept: application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8\r\n' +
        'Accept-Encoding: gzip, compress, deflate\r\n' +
        'User-Agent: audiobookshelf (+https://audiobookshelf.org; like iTMS)\r\n' +
        'Host: feeds.lagedernation.org\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/feeds/ldn-mp3.xml',
      _ended: false,
      res: null,
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'feeds.lagedernation.org',
      protocol: 'https:',
      _redirectable: [Circular *1],
      [Symbol(shapeMode)]: false,
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(errored)]: null,
      [Symbol(kHighWaterMark)]: 16384,
      [Symbol(kRejectNonStandardBodyWrites)]: false,
      [Symbol(kUniqueHeaders)]: null
    },
    _currentUrl: 'https://feeds.lagedernation.org/feeds/ldn-mp3.xml',
    _timeout: null,
    [Symbol(shapeMode)]: true,
    [Symbol(kCapture)]: false
  }
}

A discussion of this particular issue can be found here

A known workaround is to disable IPv6 entirely, but that is not generally feasible for everyone and not future proof.

The Solution:

This PR implements a custom DNS resolution mechanism that bypasses axios' default DNS handling (which relies on the OS's getaddrinfo() system call and can fail in certain environments).
My implementation hooks into the global axios client, following existing similar patterns in the codebase (analogous to the EXP_PROXY_SUPPORT feature).

When enabled via EXP_DNS_RESOLUTION=1, the following changes are applied:

1. Explicit DNS resolution before executing web requests

A request interceptor is added to the global axios instance that:

  • Attempts to resolve both IPv4 (A records via dns.resolve4()) and IPv6 (AAAA records via dns.resolve6()) addresses
  • Tries IPv4 first by default, with an optional PREFER_IPV6=1 flag to reverse the priority
  • Resolves both A and AAAA separately, allowing for independent, graceful handling of failures
  • If either resolution succeeds, the request proceeds using that IP address explicitly, rather than the hostname
  • The original hostname is preserved in the Host header of the request
  • Gracefully handles partial resolution failures - if one fails but the other succeeds, the request can still go through

2. Explicit redirect handling

Axios' built-in redirect following feature would bypass the custom DNS resolution interceptor. Thus automatic redirect handling is disabled (maxRedirects = 0) and a response interceptor is added that:

  • Explicitly follows redirects (301, 302, 303, 307, 308)
  • Ensures that redirect URLs also go through the custom DNS pre-resolution logic, by simply re-issuing the request via the axios instance
  • Prevents infinite redirect loops

The Result:

With this implementation, web requests to domains with partial DNS resolution failures will succeed when EXP_DNS_RESOLUTION=1 is set, as the custom logic ensures that at least one valid IP address is used for the request.

How have you tested this?

Tested against the paid feeds.lagedernation.org and cdn.lagedernation.org which both exhibits the partial DNS resolution failure:

  • Without EXP_DNS_RESOLUTION=1: axios requests fail completely, failing to load the RSS feed as well ass podcast audio files
  • With EXP_DNS_RESOLUTION=1: requests succeed by explicitly using IPv4 resolution
  • With EXP_DNS_RESOLUTION=1 and PREFER_IPV6=1: requests succeed, even though initial IPv6 resolution fails with SERVFAIL, but it gracefully falls back to IPv4 resolution

The implementation also handles gracefully:

  • Domains that only have IPv4 addresses
  • Domains that only have IPv6 addresses
  • Redirects that point to domains with partial DNS failures
  • Normal domains where both IPv4 and IPv6 resolve successfully

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/advplyr/audiobookshelf/pull/4885 **Author:** [@PassionateBytes](https://github.com/PassionateBytes) **Created:** 12/2/2025 **Status:** 🔄 Open **Base:** `master` ← **Head:** `fix-dns-resolution` --- ### 📝 Commits (3) - [`da040c7`](https://github.com/advplyr/audiobookshelf/commit/da040c70c7d2bae4c3a63dd0559efbd71e99e692) Implement experimental DNS pre-resolution - [`33d2839`](https://github.com/advplyr/audiobookshelf/commit/33d28392878482785394825f8303ad7dfb0a74be) Use experimental DNS resolution on redirects - [`fc81be0`](https://github.com/advplyr/audiobookshelf/commit/fc81be057c2c6b8ae7c23a5a54a2b67332c95b7b) Handle redirect loops and maximum redirect limits ### 📊 Changes **1 file changed** (+78 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `server/Server.js` (+78 -0) </details> ### 📄 Description ## Brief summary This PR introduces an optional configuration parameter `EXP_DNS_RESOLUTION` to the server, which enables a custom mechanism for pre-resolving hostnames in all `axios` web requests. This mechanism uses the dedicated `dns` package to explicitly resolve `A` and `AAAA` records. This bypasses the `getaddrinfo` syscall, which in the Alpine container environment fails entirely, if only part of the DNS query results in a `SERVFAIL`. This enables successful web requests in situations that would otherwise fail due to partial DNS resolution failures. - i.e. situations in which a DNS query resolves an `A` record of a hostname, while rejecting requests for `AAAA` records (R-Code `SERVFAIL`, or `REFUSED`). I have found that this causes the `getaddrinfo` syscall to fail entirely inside the Alpine container environment specifically, with the result that the entire web request fails. (Note that this is an alpine specific problem). Enabling this new feature with `EXP_DNS_RESOLUTION=1` will trigger explicit DNS resolution, with graceful fallback between IPv4 and IPv6. If either fails to resolve, while the other succeeds, the web request will still be allowed to go through, rather than failing entirely. ## Which issue is fixed? - https://github.com/advplyr/audiobookshelf/issues/4887 - https://github.com/advplyr/audiobookshelf/issues/4011 - https://github.com/advplyr/audiobookshelf/issues/3876 - https://github.com/advplyr/audiobookshelf/issues/3680 - https://github.com/advplyr/audiobookshelf/issues/3579 - https://github.com/advplyr/audiobookshelf/issues/3346 Additionally, a discussion of the issue server side can be found here: - https://talk.lagedernation.org/t/lage-cdn-dns-issues/27017/18?u=passionatebytes *...potentially more than just these issues - I have seen the same problem manifest for multiple different podcasts myself, so I expect to find more related issues. Will add them here when I come across more.* ## In-depth Description It appears that in some environments axios' fails to execute a web request entirely, when a domain name gets resolved only partialy. This can be the case when a DNS server is misconfigured, or overly restrictive, such that it only resolves IPv4 addresses (`A` records) successfully, while rejecting IPv6 address requests (`AAAA` records) with `SERVFAIL` (or vise versa). Consider the example below: ### Understanding Status Quo: As an example, the German News Podcast 'Lage der Nation' hosts their public feeds on `feeds.lagedernation.org` and their episodes on `cdn.lagedernation.org`. Resolving these domains returns IPv4 addresses (`A` record) successfully. However, their DNS server configuration appears to be faulty, because it consistently leads to a `SERVFAIL` when attempting to resolve for IPv6 addresses (`AAAA` records). This failure mode can be replicated in various environments: ```bash $ nslookup feeds.lagedernation.org Server: 127.0.0.53 Address: 127.0.0.53#53 Non-authoritative answer: Name: feeds.lagedernation.org Address: 139.162.176.53 ;; Got SERVFAIL reply from 127.0.0.53 ** server can't find feeds.lagedernation.org: SERVFAIL ``` In certain environments, axios appears to fail the entire web request when it encounters such a partial DNS resolution failure, even though a valid IPv4 address was returned. This results in errors like: <details><summary>Full Error Message...</summary> <p> ``` [2025-12-02 19:49:57.542] ERROR: [podcastUtils] getPodcastFeed Error AxiosError: getaddrinfo EAI_AGAIN feeds.lagedernation.org at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26) { hostname: 'feeds.lagedernation.org', syscall: 'getaddrinfo', code: 'EAI_AGAIN', errno: -3001, config: { transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false }, adapter: [Function: httpAdapter], transformRequest: [ [Function: transformRequest] ], transformResponse: [ [Function: transformResponse] ], timeout: 30000, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, env: { FormData: [Function] }, validateStatus: [Function: validateStatus], headers: { Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8', 'Accept-Encoding': 'gzip, compress, deflate', 'User-Agent': 'audiobookshelf (+https://audiobookshelf.org; like iTMS)' }, url: 'https://feeds.lagedernation.org/feeds/ldn-mp3.xml', method: 'get', responseType: 'arraybuffer', httpAgent: Agent { _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype], requests: [Object: null prototype] {}, sockets: [Object: null prototype] {}, freeSockets: [Object: null prototype] {}, keepAliveMsecs: 1000, keepAlive: false, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 0, maxCachedSessions: 100, _sessionCache: [Object], createConnection: [Function (anonymous)], [Symbol(shapeMode)]: false, [Symbol(kCapture)]: false, [Symbol(active)]: true }, httpsAgent: Agent { _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype], requests: [Object: null prototype] {}, sockets: [Object: null prototype], freeSockets: [Object: null prototype] {}, keepAliveMsecs: 1000, keepAlive: false, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 1, maxCachedSessions: 100, _sessionCache: [Object], createConnection: [Function (anonymous)], [Symbol(shapeMode)]: false, [Symbol(kCapture)]: false, [Symbol(active)]: true }, data: undefined }, request: <ref *1> Writable { _events: { close: undefined, error: [Function: handleRequestError], prefinish: undefined, finish: undefined, drain: undefined, response: [Function: handleResponse], socket: [Array], timeout: undefined, abort: undefined }, _writableState: WritableState { highWaterMark: 16384, length: 0, corked: 0, onwrite: [Function: bound onwrite], writelen: 0, bufferedIndex: 0, pendingcb: 0, [Symbol(kState)]: 17580812, [Symbol(kBufferedValue)]: null }, _maxListeners: undefined, _options: { maxRedirects: 21, maxBodyLength: 10485760, protocol: 'https:', path: '/feeds/ldn-mp3.xml', method: 'GET', headers: [Object], agent: [Agent], agents: [Object], auth: undefined, hostname: 'feeds.lagedernation.org', port: null, nativeProtocols: [Object], pathname: '/feeds/ldn-mp3.xml' }, _ended: true, _ending: true, _redirectCount: 0, _redirects: [], _requestBodyLength: 0, _requestBodyBuffers: [], _eventsCount: 3, _onNativeResponse: [Function (anonymous)], _currentRequest: ClientRequest { _events: [Object: null prototype], _eventsCount: 7, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: true, chunkedEncoding: false, shouldKeepAlive: false, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: false, sendDate: false, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: 0, _hasBody: true, _trailer: '', finished: true, _headerSent: true, _closed: false, socket: [TLSSocket], _header: 'GET /feeds/ldn-mp3.xml HTTP/1.1\r\n' + 'Accept: application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8\r\n' + 'Accept-Encoding: gzip, compress, deflate\r\n' + 'User-Agent: audiobookshelf (+https://audiobookshelf.org; like iTMS)\r\n' + 'Host: feeds.lagedernation.org\r\n' + 'Connection: close\r\n' + '\r\n', _keepAliveTimeout: 0, _onPendingData: [Function: nop], agent: [Agent], socketPath: undefined, method: 'GET', maxHeaderSize: undefined, insecureHTTPParser: undefined, joinDuplicateHeaders: undefined, path: '/feeds/ldn-mp3.xml', _ended: false, res: null, aborted: false, timeoutCb: null, upgradeOrConnect: false, parser: null, maxHeadersCount: null, reusedSocket: false, host: 'feeds.lagedernation.org', protocol: 'https:', _redirectable: [Circular *1], [Symbol(shapeMode)]: false, [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype], [Symbol(errored)]: null, [Symbol(kHighWaterMark)]: 16384, [Symbol(kRejectNonStandardBodyWrites)]: false, [Symbol(kUniqueHeaders)]: null }, _currentUrl: 'https://feeds.lagedernation.org/feeds/ldn-mp3.xml', _timeout: null, [Symbol(shapeMode)]: true, [Symbol(kCapture)]: false } } ``` </p> </details> A discussion of this particular issue can be found [here](https://talk.lagedernation.org/t/lage-cdn-dns-issues/27017/6) A [known workaround](https://github.com/advplyr/audiobookshelf/issues/3346#issuecomment-2335177670) is to disable IPv6 entirely, but that is not generally feasible for everyone and not future proof. ### The Solution: This PR implements a custom DNS resolution mechanism that bypasses axios' default DNS handling (which relies on the OS's `getaddrinfo()` system call and can fail in certain environments). My implementation hooks into the global axios client, following existing similar patterns in the codebase (analogous to the `EXP_PROXY_SUPPORT` feature). When enabled via `EXP_DNS_RESOLUTION=1`, the following changes are applied: **1. Explicit DNS resolution before executing web requests** A ***request interceptor*** is added to the global axios instance that: - Attempts to resolve both IPv4 (`A` records via `dns.resolve4()`) and IPv6 (`AAAA` records via `dns.resolve6()`) addresses - Tries IPv4 first by default, with an optional `PREFER_IPV6=1` flag to reverse the priority - Resolves both `A` and `AAAA` separately, allowing for independent, graceful handling of failures - If either resolution succeeds, the request proceeds using that IP address explicitly, rather than the hostname - The original hostname is preserved in the `Host` header of the request - Gracefully handles partial resolution failures - if one fails but the other succeeds, the request can still go through **2. Explicit redirect handling** Axios' built-in redirect following feature would bypass the custom DNS resolution interceptor. Thus automatic redirect handling is disabled (`maxRedirects = 0`) and a ***response interceptor*** is added that: - Explicitly follows redirects (301, 302, 303, 307, 308) - Ensures that redirect URLs also go through the custom DNS pre-resolution logic, by simply re-issuing the request via the axios instance - Prevents infinite redirect loops ### The Result: With this implementation, web requests to domains with partial DNS resolution failures will succeed when `EXP_DNS_RESOLUTION=1` is set, as the custom logic ensures that at least one valid IP address is used for the request. ## How have you tested this? Tested against the paid `feeds.lagedernation.org` and `cdn.lagedernation.org` which both exhibits the partial DNS resolution failure: - Without `EXP_DNS_RESOLUTION=1`: axios requests fail completely, failing to load the RSS feed as well ass podcast audio files - With `EXP_DNS_RESOLUTION=1`: requests succeed by explicitly using IPv4 resolution - With `EXP_DNS_RESOLUTION=1` and `PREFER_IPV6=1`: requests succeed, even though initial IPv6 resolution fails with `SERVFAIL`, but it gracefully falls back to IPv4 resolution The implementation also handles gracefully: - Domains that only have IPv4 addresses - Domains that only have IPv6 addresses - Redirects that point to domains with partial DNS failures - Normal domains where both IPv4 and IPv6 resolve successfully --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
adam added the pull-request label 2026-04-25 00:19:26 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/audiobookshelf#4365