{"record":{"id":"40dfb2f0ad263517","repo":"jackwener/OpenCLI","slug":"error-40dfb2","errorCode":null,"errorMessage":"未获取到课程列表","messagePattern":"未获取到课程列表","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/chaoxing/assignments.js","lineNumber":31,"sourceCode":"        {\n            name: 'status',\n            type: 'string',\n            default: 'all',\n            choices: ['all', 'pending', 'submitted', 'graded'],\n            help: '按状态过滤',\n        },\n        { name: 'limit', type: 'int', default: 20, help: '最大返回数量' },\n        { name: 'timeout', type: 'int', required: false, default: 90, help: 'Max seconds for the overall command (default: 90)' },\n    ],\n    columns: ['rank', 'course', 'title', 'deadline', 'status', 'score'],\n    func: async (page, kwargs) => {\n        const { course: courseFilter, status: statusFilter = 'all', limit = 20 } = kwargs;\n        // 1. Establish session\n        await initSession(page);\n        // 2. Get courses\n        const courses = await getCourses(page);\n        if (!courses.length)\n            throw new AuthRequiredError('mooc2-ans.chaoxing.com', '未获取到课程列表');\n        const filtered = courseFilter\n            ? courses.filter(c => c.title.includes(courseFilter))\n            : courses;\n        if (courseFilter && !filtered.length) {\n            throw new Error(`未找到匹配「${courseFilter}」的课程`);\n        }\n        // 3. Per-course: enter → click 作业 tab → navigate to iframe → parse\n        const allRows = [];\n        for (const c of filtered) {\n            try {\n                await enterCourse(page, c);\n                const iframeUrl = await getTabIframeUrl(page, '作业');\n                if (!iframeUrl)\n                    continue;\n                await page.goto(iframeUrl);\n                await page.wait(2);\n                const rows = await parseAssignmentsFromDom(page, c.title);\n                allRows.push(...rows);","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/chaoxing/assignments.js#L13-L49","documentation":"The assignments command first establishes a Chaoxing session and calls getCourses; if the course list comes back empty it throws AuthRequiredError('mooc2-ans.chaoxing.com','未获取到课程列表') because an authenticated account always has at least one course — an empty list means the session is not really logged in.","triggerScenarios":"Running assignments without a valid Chaoxing login (expired cookies, failed initSession, captcha not solved) so getCourses returns zero rows.","commonSituations":"Stale saved cookies after password change; login page redirect not detected; new account with no enrolled courses; school SSO session expiring.","solutions":["Re-authenticate: run the chaoxing login command to refresh cookies, then retry assignments","Verify cookies include UID/_uid/cx_p_token for i.chaoxing.com before running","Confirm the account actually has enrolled courses at mooc2-ans.chaoxing.com","Check whether getCourses was redirected to the login page (initSession may have silently failed)"],"exampleFix":"// before\nconst courses = await getCourses(page);\nif (!courses.length) throw new AuthRequiredError('mooc2-ans.chaoxing.com', '未获取到课程列表');\n// after\nlet courses = await getCourses(page);\nif (!courses.length) { await login(page); courses = await getCourses(page); }\nif (!courses.length) throw new AuthRequiredError('mooc2-ans.chaoxing.com', '未获取到课程列表');","handlingStrategy":"try-catch","validationCode":"const cookies = await page.getCookies({ url: 'https://i.chaoxing.com' });\nif (!cookies.some(c => /^(UID|_uid|cx_p_token)$/i.test(c.name) && c.value)) await chaoxingLogin(page);","typeGuard":"null","tryCatchPattern":"try { const rows = await assignments(page, kwargs); }\ncatch (e) {\n  if (e instanceof AuthRequiredError && e.message.includes('未获取到课程列表')) { await login(page); return assignments(page, kwargs); }\n  throw e;\n}","preventionTips":["Run the identity/whoami check before assignments to confirm the session","Persist cookies via storageState across runs","Ensure the account has at least one enrolled course","Catch AuthRequiredError and re-login once automatically"],"tags":["chaoxing","auth","empty-list","session"],"backgroundTag":"auth-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}