{"record":{"id":"1e605a35ce12e5c4","repo":"DIYgod/RSSHub","slug":"response-msg-1e605a","errorCode":null,"errorMessage":"response.msg","messagePattern":"response\\.msg","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/sdo/ff14risingstones/utils.tsx","lineNumber":134,"sourceCode":"    }\n}\n\nexport function request(url: string, options?: RequestInit) {\n    return ofetch(url, {\n        ...options,\n        headers: {\n            Cookie: `ff14risingstones=${config.sdo.ff14risingstones}`,\n            'User-Agent': config.sdo.ua!,\n            ...options?.headers,\n        },\n    });\n}\n\nexport async function requestAPI<T = any>(url: string, options?: RequestInit) {\n    const response = (await request(url, options)) as BaseResponse<T>;\n\n    if (response.code !== 10000) {\n        throw new Error(response.msg);\n    }\n    return response.data;\n}\n\nexport async function generatePostFeeds(posts: UserPost[]) {\n    return await Promise.all(\n        posts.map(async (post) => {\n            const detail = await getPostsDetail(post.posts_id);\n            return {\n                title: `[${post.part_name}] ${post.title}`,\n                link: `${INDEX_URL}#/post/detail/${post.posts_id}`,\n                description: detail?.contentInfo.content,\n                pubDate: timezone(parseDate(post.created_at), 8),\n                updated: detail?.updated_at ? timezone(parseDate(detail.updated_at), 8) : undefined,\n                guid: `sdo/ff14risingstones/posts:${post.posts_id}`,\n                author: `${post.character_name}@${post.group_name}`,\n            } as DataItem;\n        })","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/sdo/ff14risingstones/utils.tsx#L116-L152","documentation":"Thrown by requestAPI when the SDO API response code is not 10000 (the success code). The SDO API returns a BaseResponse envelope with code + msg; any non-success code is treated as a failure and the server-supplied msg is re-thrown verbatim as the error message. Because msg comes from the upstream, the thrown message is dynamic.","triggerScenarios":"A request to an ff14risingstones API endpoint returns code !== 10000 (e.g. auth failure, expired cookie, rate limit, parameter error). response.msg is then a server-defined string such as '未登录' (not logged in) or a validation message.","commonSituations":"The ff14risingstones session cookie expired; the UA in config.sdo.ua is blocked; the requested resource id is invalid; SDO deployed a new API contract returning a different success code; response.msg is undefined for some codes, producing a vague 'undefined' error.","solutions":["Refresh the SDO_FF14RISINGSTONES cookie (re-login) and clear any cached auth state.","Match config.sdo.ua to a currently-accepted browser User-Agent.","Log response.code alongside msg to identify the specific failure class (auth vs validation vs rate-limit).","If msg is undefined for a known code, add a code->message map so the error is actionable."],"exampleFix":"// before\nif (response.code !== 10000) {\n    throw new Error(response.msg);\n}\n\n// after — include the code and URL so the failure is identifiable even when msg is empty\nif (response.code !== 10000) {\n    throw new Error(`ff14risingstones API error (code ${response.code}) at ${url}: ${response.msg ?? 'no message'}`);\n}","handlingStrategy":"try-catch","validationCode":"// Map known non-success codes to actionable messages before the generic throw.\nconst SDO_ERROR_MAP: Record<number, string> = {\n    401: 'Not logged in — refresh SDO_FF14RISINGSTONES cookie',\n    429: 'Rate limited by SDO API',\n};\nif (response.code !== 10000) {\n    throw new Error(SDO_ERROR_MAP[response.code] ?? `SDO API error (code ${response.code}): ${response.msg ?? 'no message'}`);\n}","typeGuard":"const isBaseResponse = (r: any): r is { code: number; msg?: string; data?: unknown } =>\n    r !== null && typeof r === 'object' && typeof r.code === 'number';","tryCatchPattern":"try {\n    data = await requestAPI(url, options);\n} catch (e) {\n    if (e instanceof Error && /not logged in|未登录|登录/.test(e.message)) {\n        // cookie likely expired — purge and re-auth, then retry once\n        await refreshSdoSession();\n        data = await requestAPI(url, options);\n    } else if (e instanceof Error && /rate|频繁|429/.test(e.message)) {\n        // back off via cache/queue rather than retrying immediately\n        throw e;\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always include response.code in the rethrown message so the failure class is identifiable.","Refresh the ff14risingstones cookie automatically when an auth-related code appears.","Log the upstream msg only at debug level to avoid leaking sensitive detail."],"tags":["api","auth","session","upstream-error","dynamic-message"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}