DIYgod/RSSHub · error · ConfigNotFoundError

QWeather RSS is disabled due to the lack of <a href="https:/

Error message

QWeather RSS is disabled due to the lack of <a href="https://docs.rsshub.app/zh/install/config#%E5%92%8C%E9%A3%8E%E5%A4%A9%E6%B0%94">relevant config</a>

What it means

The QWeather 3-day forecast route requires both `config.hefeng.key` (the API key) and `config.hefeng.apiHost` (the API host, e.g. devapi.qweather.com) to be set in the RSSHub instance configuration. If either is falsy, a `ConfigNotFoundError` is thrown before any cache lookup or HTTP call. This is the documented gate for any Hefeng/QWeather route — without credentials the upstream APIs return 403/401.

Source

Thrown at lib/routes/qweather/3days.ts:42

                name: 'HEFENG_API_HOST',
                description: 'This is required after 2026/01/01: https://blog.qweather.com/announce/public-api-domain-change-to-api-host/',
            },
        ],
        requirePuppeteer: false,
        antiCrawler: false,
        supportBT: false,
        supportPodcast: false,
        supportScihub: false,
    },
    name: '近三天天气',
    maintainers: ['Rein-Ou', 'la3rence'],
    handler,
    description: '获取订阅近三天天气预报',
};

async function handler(ctx) {
    if (!config.hefeng.key || !config.hefeng.apiHost) {
        throw new ConfigNotFoundError('QWeather RSS is disabled due to the lack of <a href="https://docs.rsshub.app/zh/install/config#%E5%92%8C%E9%A3%8E%E5%A4%A9%E6%B0%94">relevant config</a>');
    }

    const WEATHER_API = `https://${config.hefeng.apiHost}/v7/weather/3d`;
    const AIR_QUALITY_API = `https://${config.hefeng.apiHost}/v7/air/5d`;
    const CIRY_LOOKUP_API = `https://${config.hefeng.apiHost}/geo/v2/city/lookup`;

    const id = await cache.tryGet('qweather:' + ctx.req.param('location') + ':id', async () => {
        const response = await got(`${CIRY_LOOKUP_API}?location=${ctx.req.param('location')}&key=${config.hefeng.key}`);
        return response.data.location[0].id;
    });
    const weatherData = await cache.tryGet(
        'qweather:' + ctx.req.param('location'),
        async () => {
            const response = await got(`${WEATHER_API}?key=${config.hefeng.key}&location=${id}`);
            return response.data;
        },
        config.cache.contentExpire,
        false

View on GitHub (pinned to bed535e087)

Solutions

  1. Set both environment variables on the RSSHub host: `HEFENG_KEY=<your key>` and `HEFENG_APIHOST=<devapi.qweather.com or api.qweather.com>`.
  2. Restart the RSSHub process after editing config so it reloads `config.hefeng`.
  3. If you cannot obtain a key, use the public rsshub.app instance only if its operator has configured QWeather — otherwise this route will remain unavailable.

Example fix

// before
# .env — missing or partial
HEFENG_KEY=

// after
# .env
HEFENG_KEY=abc123def456
HEFENG_APIHOST=devapi.qweather.com
Defensive patterns

Strategy: validation

Validate before calling

if (!config.hefeng?.key || !config.hefeng?.apiHost) {
    return ctx.body('QWeather route requires HEFENG_KEY and HEFENG_APIHOST.', 503);
}

Type guard

const hasHefengConfig = (c: typeof config): boolean =>
    Boolean(c.hefeng?.key) && Boolean(c.hefeng?.apiHost);

Try / catch

try {
    // handler body
} catch (e) {
    if (e instanceof ConfigNotFoundError) { /* surface a setup hint */ }
    throw e;
}

Prevention

When it happens

Trigger: A self-hosted RSSHub instance whose operator has not populated `HEFENG_KEY` and `HEFENG_APIHOST` env vars (or the equivalent in config). The check fires at the very top of `handler`, before the city-lookup cache call.

Common situations: Fresh deploy of RSSHub without environment variables set; operator copied only one of the two required values; key expired and was deleted from config.

Related errors


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