DIYgod/RSSHub · error · Error

Invalid URL

Error message

Invalid URL

What it means

The url parameter is validated against ^https?:\/\/[^\s#$./?].\S*$ (case-insensitive) before any fetch. It requires an http/https scheme, no whitespace/fragment/query metacharacters in the first character, and at least one more character after the scheme prefix. A non-match throws a plain Invalid URL.

Source

Thrown at lib/routes/wordpress/index.ts:21

import { config } from '@/config';
import ConfigNotFoundError from '@/errors/types/config-not-found';
import type { Data, Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import parser from '@/utils/rss-parser';

import { apiSlug, bakeFilterSearchParams, bakeFiltersWithPair, bakeUrl, fetchData, getFilterParamsForUrl, parseFilterStr } from './util';

async function handler(ctx) {
    const { url = 'https://wordpress.org/news', filter } = ctx.req.param();
    const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 50;

    if (!config.feature.allow_user_supply_unsafe_domain) {
        throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
    }

    if (!/^https?:\/\/[^\s#$./?].\S*$/i.test(url)) {
        throw new Error('Invalid URL');
    }

    const cdn = config.wordpress.cdnUrl;
    const rootUrl = url;

    const filters = parseFilterStr(filter);
    const filtersWithPair = await bakeFiltersWithPair(filters, rootUrl);

    const searchParams = bakeFilterSearchParams(filters, 'name', false);
    const apiSearchParams = bakeFilterSearchParams(filtersWithPair, 'id', true);

    apiSearchParams.append('_embed', 'true');
    apiSearchParams.append('per_page', String(limit));

    const apiUrl = bakeUrl(`${apiSlug}/posts`, rootUrl, apiSearchParams);
    const currentUrl = bakeUrl(getFilterParamsForUrl(filtersWithPair) ?? '', rootUrl, searchParams);

    try {

View on GitHub (pinned to bed535e087)

Solutions

  1. Ensure the url starts with http:// or https:// and has no spaces.
  2. URL-encode the whole url when placing it in the route path (encodeURIComponent).
  3. Use the documented default by omitting url, or pass a fully-qualified wordpress root.

Example fix

// before
/wordpress/wordpress.org/news
// after
/wordpress/https%3A%2F%2Fwordpress.org%2Fnews
Defensive patterns

Strategy: validation

Validate before calling

function isValidWordpressUrl(u: string): boolean {
    return /^https?:\/\/[^\s#$./?].\S*$/i.test(u);
}
if (!isValidWordpressUrl(url)) {
    throw new InvalidParameterError('url must be a fully-qualified http(s) URL');
}

Type guard

function isWordpressInvalidUrlError(e: unknown): boolean {
    return e instanceof Error && e.message === 'Invalid URL';
}

Try / catch

try {
    return await wordpressHandler(ctx);
} catch (e) {
    if (isWordpressInvalidUrlError(e)) {
        return ctx.json({ error: 'Invalid url parameter. Encode it with encodeURIComponent and include the scheme.' }, 400);
    }
    throw e;
}

Prevention

When it happens

Trigger: Passing a url without a scheme (example.com), with spaces, with a leading # or ? , a single-char host, or a malformed/encoded value that breaks the pattern at wordpress/index.ts:20.

Common situations: User forgot to URL-encode the value, passed a bare domain, or the leading char is one of the excluded set (#$./?).

Related errors


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