DIYgod/RSSHub · error · Error

${statusMessage}

Error message

${statusMessage}

What it means

Thrown by the ke.com (贝壳) research-results handler when the response body's `status` field (a business-level code in the JSON envelope, not the HTTP status) is not 200. The handler destructures {status, statusMessage, data} from got's response.data and re-throws statusMessage as a plain Error. Because got already threw on transport errors, reaching this line means HTTP succeeded but the application envelope reported failure.

Source

Thrown at lib/routes/ke/results.ts:44

    url: 'www.research.ke.com/researchResults',
};

async function handler() {
    const response = await got({
        method: 'post',
        url: 'https://research.ke.com/apis/consumer-access/index/contents/page',
        headers: {
            Referer: 'https://research.ke.com/ResearchResults',
        },
        json: {
            pageIndex: 1,
            pageSize: 9,
        },
    });

    const { status, statusMessage, data } = response;
    if (status !== 200) {
        throw new Error(statusMessage);
    }

    const { list } = data.data;

    return {
        title: '房地产行业研究报告',
        link: 'https://research.ke.com/ResearchResults',
        description: '研究成果',
        item: list.map((item) => ({
            title: item.title,
            link: `https://research.ke.com/${item.contentTypeId}/ArticleDetail?id=${item.id}`,
            author: item.author,
            description: item.guideReading,
            pubDate: parseDate(item.publishTime),
        })),
    };
}

View on GitHub (pinned to bed535e087)

Solutions

  1. Retry after a short delay — many statusMessage failures are transient
  2. Verify the endpoint and payload still match by calling the POST manually with the same JSON body
  3. If 贝克 changed the success contract, update the status!==200 check in lib/routes/ke/results.ts:43
  4. Surface the status code alongside the message so transient vs permanent failures are distinguishable

Example fix

// before
if (status !== 200) {
    throw new Error(statusMessage);
}
// after
if (status !== 200) {
    throw new Error(`ke.com research API failed: status=${status}, message=${statusMessage}`);
}
Defensive patterns

Strategy: retry

Validate before calling

async function fetchKeResults(maxRetries = 2): Promise<Data> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const resp = await got({ method: 'post', url: 'https://research.ke.com/apis/consumer-access/index/contents/page', json: { pageIndex: 1, pageSize: 9 } });
    if (resp.status === 200 && resp.data?.status === 200) return buildData(resp.data.data.list);
    if (attempt === maxRetries) throw new Error(`ke API status=${resp.data?.status}: ${resp.data?.statusMessage}`);
    await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
  }
  throw new Error('unreachable');
}

Type guard

interface KeEnvelope { status: number; statusMessage?: string; data?: { data?: { list?: unknown[] } } }
function isKeSuccess(v: unknown): v is KeEnvelope {
  return typeof v === 'object' && v !== null && (v as KeEnvelope).status === 200;
}

Try / catch

try { return await handler(); }
catch (e) {
  if (e instanceof Error && /statusMessage|ke API/i.test(e.message)) {
    // business-level failure — retry with backoff, then degrade gracefully
    await new Promise((r) => setTimeout(r, 2000));
    return await handler();
  }
  throw e;
}

Prevention

When it happens

Trigger: POST to https://research.ke.com/apis/consumer-access/index/contents/page with {pageIndex:1, pageSize:9} returns 200 OK but a JSON body whose `status` is e.g. 500, 403, or a business error code, with `statusMessage` describing it.

Common situations: 贝克 research API rate-limiting or under maintenance; anti-crawler returning a business-level denial; upstream schema change where status is no longer the success indicator; temporary backend 5xx surfacing in the envelope.

Related errors


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