DIYgod/RSSHub · warning · InvalidParameterError

unsupported range: ${typ}

Error message

unsupported range: ${typ}

What it means

parseRange parses an HTTP Range header and only supports the 'bytes' range unit (RFC 7233). If the token before '=' is anything other than 'bytes' (or the header is malformed so the split yields a different unit), it throws InvalidParameterError with the offending unit.

Source

Thrown at lib/routes/telegram/channel-media.ts:121

        }
    );
    for await (const chunk of chunks) {
        if (skip >= chunk.length) {
            skip -= chunk.length;
            continue;
        }
        yield skip ? chunk.subarray(skip) : chunk;
        skip = 0;
    }
}

function parseRange(range: string, length: bigInt.BigInteger) {
    if (!range) {
        return [];
    }
    const [typ, segstr] = range.split('=', 2);
    if (typ !== 'bytes') {
        throw new InvalidParameterError(`unsupported range: ${typ}`);
    }
    const segs = segstr.split(',').map((s) => s.trim());
    const parsedSegs: bigInt.BigInteger[][] = [];
    for (const seg of segs) {
        const range = seg
            .split('-', 2)
            .filter((v) => !!v)
            .map((v) => bigInt(v));
        if (range.length < 2) {
            if (seg.startsWith('-')) {
                range.unshift(bigInt(0));
            } else {
                range.push(length.subtract(bigInt(1)));
            }
        }
        parsedSegs.push(range);
    }
    return parsedSegs;

View on GitHub (pinned to bed535e087)

Solutions

  1. Have the client send a standards-compliant 'Range: bytes=start-end' header.
  2. In parseRange, treat an unknown unit as 'no range' (return []) and serve the full content with 200 instead of throwing, if partial content is optional.
  3. If a non-bytes unit is genuinely needed, extend parseRange to handle it explicitly rather than relying on the throw.

Example fix

// before
const [typ, segstr] = range.split('=', 2);
if (typ !== 'bytes') {
    throw new InvalidParameterError(`unsupported range: ${typ}`);
}

// after: ignore unsupported units and serve full content
const [typ, segstr] = range.split('=', 2);
if (typ !== 'bytes') {
    return []; // caller treats empty as 'no range'
}
Defensive patterns

Strategy: validation

Validate before calling

function isBytesRange(range: string | undefined): boolean {
    return !range || range.toLowerCase().startsWith('bytes=');
}
// reject/ignore the request early if the client sent a non-bytes range

Type guard

function isBytesRangeHeader(range: string | undefined): boolean {
    return !range || range.trim().toLowerCase().startsWith('bytes=');
}

Try / catch

try {
    const segs = parseRange(rangeHeader, length);
    // serve partial or full content based on segs
} catch (e) {
    if (e instanceof InvalidParameterError && /unsupported range/.test(e.message)) {
        // ignore the unsupported Range and serve the full content with 200
        return fullContent();
    }
    throw e;
}

Prevention

When it happens

Trigger: A client (media player, browser, downloader) sends a Range header like 'items=0-100', 'time=0-5', or a malformed 'range=0-100'. The split('=') yields a unit that is not 'bytes'.

Common situations: A custom downloader sends a non-standard range unit; a buggy client prefixes the header value incorrectly; a proxy rewrites Range into an unsupported form.

Related errors


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