DIYgod/RSSHub · error · ConfigNotFoundError

Invalid username (or email) or password for nhentai torrent

Error message

Invalid username (or email) or password for nhentai torrent download

What it means

A ConfigNotFoundError thrown after the nhentai login flow completes but yields no session cookie. getCookie POSTs credentials and treats a non-302 response as auth failure, caching an empty cookie string; getTorrents then re-throws it as a config error so operators know the stored credentials are wrong rather than missing.

Source

Thrown at lib/routes/nhentai/util.tsx:110

const getSimple = async (url) => {
    const data = await fetchPage(url);
    const $ = load(data);

    return $('.gallery a.cover')
        .toArray()
        .map((ele) => parseSimpleDetail($(ele)));
};

const getDetails = (cache, simples, limit) => Promise.all(simples.slice(0, limit).map((simple) => cache.tryGet(simple.link, () => getDetail(simple))));

const getTorrents = async (cache, simples, limit) => {
    if (!config.nhentai || !config.nhentai.username || !config.nhentai.password) {
        throw new ConfigNotFoundError('nhentai RSS with torrents is disabled due to the lack of <a href="https://docs.rsshub.app/deploy/config#route-specific-configurations">relevant config</a>');
    }
    const cookie = await getCookie(config.nhentai.username, config.nhentai.password, cache);
    if (!cookie) {
        throw new ConfigNotFoundError('Invalid username (or email) or password for nhentai torrent download');
    }
    return getTorrentWithCookie(cache, simples, cookie, limit);
};
const getTorrentWithCookie = (cache, simples, cookie, limit) => Promise.all(simples.slice(0, limit).map((simple) => cache.tryGet(simple.link + 'download', () => getTorrent(simple, cookie))));

const parseSimpleDetail = ($ele) => {
    const link = new URL($ele.attr('href'), baseUrl).href;
    const thumb = $ele.children('img');
    const thumbSrc = thumb.attr('data-src') || thumb.attr('src');
    const highResoThumbSrc = thumbSrc
        .replace('thumb', '1')
        .replace(/t(\d+)\.nhentai\.net/, 'i$1.nhentai.net')
        .replace('.webp.webp', '.webp');
    return {
        title: $ele.children('.caption').text(),
        link,
        description: `<img src="${highResoThumbSrc}">`,
    };

View on GitHub (pinned to bed535e087)

Solutions

  1. Verify NHENTAI_USERNAME/NHENTAI_PASSWORD by logging in manually at https://nhentai.net/login/.
  2. Clear the cached bad cookie (redis/redis-like cache key 'nhentai:cookie') so the next request re-attempts login with the corrected credentials.
  3. If credentials are correct but login still returns non-302, inspect the login response shape — the csrfmiddlewaretoken regex or set-cookie parsing in getCookie may need updating after an upstream change.
  4. Rotate the password if the account was compromised or rate-limited.

Example fix

// before
if (!cookie) {
    throw new ConfigNotFoundError('Invalid username (or email) or password for nhentai torrent download');
}

// after — operational fix: purge the stale bad-cookie cache entry, then let it re-login
// redis-cli DEL nhentai:cookie   (or your cache backend equivalent)
Defensive patterns

Strategy: retry

Validate before calling

// Validate credentials reachability before relying on the cookie cache.
async function nhentaiLoginWorks(): Promise<boolean> {
    if (!config.nhentai?.username || !config.nhentai?.password) return false;
    // attempt a fresh login (bypass cache) once on startup/config-change
    const cookie = await getCookie(config.nhentai.username, config.nhentai.password, cache);
    return Boolean(cookie);
}

Type guard

const isNonEmptyCookie = (c: unknown): c is string => typeof c === 'string' && c.length > 0;

Try / catch

try {
    items = await getTorrents(cache, simples, limit);
} catch (e) {
    if (e instanceof ConfigNotFoundError && /Invalid username/.test(e.message)) {
        // purge the cached bad cookie and retry once with fresh login
        await cache.set('nhentai:cookie', '');
        items = await getTorrents(cache, simples, limit); // single retry
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: getCookie posts to https://nhentai.net/login/ with the configured username/password and the login response status is not 302 (redirect on success), so an empty cookie is cached and returned. The next check `if (!cookie)` triggers the error.

Common situations: NHENTAI_PASSWORD is stale/wrong; the account is locked or flagged; nhentai changed its CSRF/cookie scheme so the parsed csrfmiddlewaretoken or set-cookie headers are malformed (got() would then throw earlier, but a login that returns 200 with a login-failed page yields the empty-cookie path); the cached empty cookie (key 'nhentai:cookie') is still within its 3-day TTL and masks a corrected password until it expires.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/4a52fe32fa059dc6. Report an issue: GitHub.