DIYgod/RSSHub · error · TypeError

TechFlow API returned an invalid article list.

Error message

TechFlow API returned an invalid article list.

What it means

Thrown by the article-list fetcher when requestApi<ApiListResponse<Article>> succeeds (no anti-crawler string) but response.data is not an Array. The contract with GET /client/articles is that .data is the list of articles; a non-array means the API returned an error object, a paginated wrapper, or a shape change rather than a list.

Solutions

  1. Inspect the actual response body (log response) to see whether the API now nests the array (e.g. response.data.list or response.data.items) and update the guard/parse accordingly.
  2. Confirm category_id is valid before sending it; an invalid category often yields an error object instead of a list.
  3. If the API genuinely returned an empty-but-valid list shape, check for a different list field name and update the ApiListResponse type plus the Array.isArray target.
  4. Add a defensive log of the raw response when Array.isArray fails so future shape changes are diagnosable instead of opaque.

Example fix

// before
if (!Array.isArray(response.data)) {
    throw new TypeError('TechFlow API returned an invalid article list.');
}

// after: also accept a nested list and log the unexpected shape
const list = Array.isArray(response.data) ? response.data : response.data?.list;
if (!Array.isArray(list)) {
    throw new TypeError(`TechFlow API returned an invalid article list: ${JSON.stringify(response).slice(0, 200)}`);
}
Defensive patterns

Strategy: type-guard

Validate before calling

function isArticleList(r: unknown): r is { data: unknown[] } {
    return !!r && typeof r === 'object' && Array.isArray((r as any).data);
}
// const response = await requestApi<ApiListResponse<Article>>(...);
// if (!isArticleList(response)) { /* handle */ }

Type guard

function isArticleListResponse(r: unknown): r is { data: Article[] } {
    return typeof r === 'object' && r !== null && Array.isArray((r as { data?: unknown }).data);
}

Try / catch

let response;
try {
    response = await requestApi<ApiListResponse<Article>>('/client/articles', link, params);
} catch (e) {
    if (e instanceof TypeError && /invalid article list/.test(e.message)) {
        // log and degrade to empty feed instead of failing the route
        return [];
    }
    throw e;
}
if (!isArticleListResponse(response)) { return []; }

Prevention

When it happens

Trigger: Calling GET {apiRoot}/client/articles with page=1 and page_size=limit (optionally category_id) where the server replies with {code, msg, ...} or {data: {...}} instead of {data: [...]}. Also fires if the endpoint starts returning an object due to a category_id that resolves to a non-list response.

Common situations: TechFlow ships an API version bump that wraps lists differently; an invalid/deleted category_id makes the API return an error envelope that still HTTP-200s; ofetch/got auto-parses JSON so a temporary maintenance page served as JSON also lands here.

Related errors


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

Appendix: source

Thrown at lib/routes/techflowpost/utils.ts:154

        updated: newsflash.updated_at ? parseDate(newsflash.updated_at) : undefined,
        description: content || newsflash.abstract,
    };
}

async function getArticleItems({ category, limit }: { category?: string; limit: string | number }) {
    const link = `${rootUrl}/${locale}/article`;
    const searchParams: Record<string, string | number> = {
        page: 1,
        page_size: limit,
    };

    if (category) {
        searchParams.category_id = category;
    }

    const response = await requestApi<ApiListResponse<Article>>('/client/articles', link, searchParams);
    if (!Array.isArray(response.data)) {
        throw new TypeError('TechFlow API returned an invalid article list.');
    }

    return await Promise.all(
        response.data.map((article) =>
            cache.tryGet(`techflowpost:article:${article.id}`, async () => {
                const itemLink = `${rootUrl}/${locale}/article/${article.id}`;
                const detail = await requestApi<ArticleDetailResponse>(`/client/articles/${article.id}`, itemLink);

                return getArticleItem(article, detail.article?.content);
            })
        )
    );
}

async function getNewsflashItems(limit: string | number) {
    const link = `${rootUrl}/${locale}/newsletter`;
    const response = await requestApi<ApiListResponse<Newsflash>>('/client/newsflashes', link, {
        page: 1,

View on GitHub (pinned to bed535e087)