DIYgod/RSSHub · error · ConfigNotFoundError
无权访问 id 为 ${catalogId} 的 List(可能是未设置 Cookie 或 Cookie 已过期)
Error message
无权访问 id 为 ${catalogId} 的 List(可能是未设置 Cookie 或 Cookie 已过期) What it means
ConfigNotFoundError thrown by the Medium 'list' route when the getUserCatalogMainContentQuery response's __typename is 'Forbidden'. Medium's API explicitly denied access to the requested catalog/list — almost always because no cookie or an expired cookie was sent for that user.
Source
Thrown at lib/routes/medium/list.ts:42
handler,
description: `The List ID is the last part of the URL after \`-\`, for example, the username in <https://medium.com/@imsingee/list/collection-7e67004f23f9> is \`imsingee\`, and the ID is \`7e67004f23f9\`.
::: warning
To access private lists, only self-hosting is supported.
:::`,
};
async function handler(ctx) {
const user = ctx.req.param('user');
const catalogId = ctx.req.param('catalogId');
const cookie = config.medium.cookies[user];
const catalog = await getUserCatalogMainContentQuery(user, catalogId, cookie);
ctx.set('json', catalog);
if (catalog && catalog.__typename === 'Forbidden') {
throw new ConfigNotFoundError(`无权访问 id 为 ${catalogId} 的 List(可能是未设置 Cookie 或 Cookie 已过期)`);
}
if (!catalog || !catalog.itemsConnection) {
throw new InvalidParameterError(`id 为 ${catalogId} 的 List 不存在`);
}
const name = catalog.name;
const urls = catalog.itemsConnection.items.map((item) => item.entity.mediumUrl);
const parsedArticles = await Promise.all(urls.map((url) => parseArticle(ctx, url)));
return {
title: `List: ${name}`,
link: `https://medium.com/@${user}/list/${catalogId}`,
item: parsedArticles,
};
}
View on GitHub (pinned to bed535e087)
Solutions
- Set config.medium.cookies[user] (MEDIUM_COOKIES env) with a valid session cookie from the account that owns or follows the list.
- Re-login and refresh the cookie if expired.
- Confirm the list is accessible by the logged-in account in a browser.
- Verify both :user and :catalogId path parameters are correct.
Example fix
// before // config.medium.cookies[user] unset -> cookie=undefined -> Forbidden // after MEDIUM_COOKIES=johndoe=sid%3A1%3A...
Defensive patterns
Strategy: validation
Validate before calling
const cookie = config.medium.cookies?.[user];
if (!cookie) {
throw new ConfigNotFoundError(`Set Medium cookie for ${user} to access private lists`);
} Type guard
function hasMediumCookie(user: string): boolean {
return Boolean(config.medium.cookies?.[user]);
} Try / catch
const catalog = await getUserCatalogMainContentQuery(user, catalogId, cookie);
if (catalog?.__typename === 'Forbidden') {
throw new ConfigNotFoundError('Access denied — refresh the Medium cookie');
} Prevention
- Always configure a cookie for list routes.
- Confirm list visibility for the logged-in account.
- Differentiate Forbidden (cookie) from missing (catalogId) in operator alerts.
When it happens
Trigger: getUserCatalogMainContentQuery(user, catalogId, cookie) resolves to an object whose __typename === 'Forbidden'. This is Medium's access-control rejection for private or member-gated lists.
Common situations: config.medium.cookies[user] missing (cookie is undefined, sent as undefined); cookie expired so Medium treats the request as anonymous; the list is private to a different account; list was deleted/locked.
Related errors
- 缺少 Medium 用户 ${user} 登录后的 Cookie 值
- Medium 用户 ${user} 的 Cookie 无效或已过期
- 缺少 Medium 用户 ${user} 登录后的 Cookie 值
- Medium 用户 ${user} 的 Cookie 无效或已过期
- 缺少 Medium 用户 ${user} 登录后的 Cookie 值
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/4e040ad55020b0c8.
Report an issue: GitHub.