{"record":{"id":"48280ca04f9065a6","repo":"jackwener/OpenCLI","slug":"photo-id-requestedphotoid-was-not-found-under-s","errorCode":null,"errorMessage":"Photo ID ${requestedPhotoId} was not found under subject ${subjectId}. Try \"douban photos ${subjectId} -f json\" first.","messagePattern":"Photo ID (.+?) was not found under subject (.+?)\\. Try \"douban photos (.+?) -f json\" first\\.","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/douban/download.js","lineNumber":44,"sourceCode":"    ],\n    columns: ['index', 'title', 'status', 'size'],\n    func: async (page, kwargs) => {\n        const subjectId = normalizeDoubanSubjectId(String(kwargs.id || ''));\n        const output = String(kwargs.output || './douban-downloads');\n        const requestedPhotoId = String(kwargs['photo-id'] || '').trim();\n        const loadOptions = {\n            type: String(kwargs.type || 'Rb'),\n        };\n        if (requestedPhotoId)\n            loadOptions.targetPhotoId = requestedPhotoId;\n        else\n            loadOptions.limit = Number(kwargs.limit) || 120;\n        const data = await loadDoubanSubjectPhotos(page, subjectId, loadOptions);\n        const photos = requestedPhotoId\n            ? data.photos.filter((photo) => photo.photoId === requestedPhotoId)\n            : data.photos;\n        if (requestedPhotoId && !photos.length) {\n            throw new EmptyResultError('douban download', `Photo ID ${requestedPhotoId} was not found under subject ${subjectId}. Try \"douban photos ${subjectId} -f json\" first.`);\n        }\n        const outputDir = path.join(output, subjectId);\n        fs.mkdirSync(outputDir, { recursive: true });\n        const results = [];\n        for (const photo of photos) {\n            const filename = buildDoubanPhotoFilename(subjectId, photo);\n            const destPath = path.join(outputDir, filename);\n            const result = await httpDownload(photo.imageUrl, destPath, {\n                headers: { Referer: photo.detailUrl || `https://movie.douban.com/subject/${subjectId}/photos?type=${encodeURIComponent(String(kwargs.type || 'Rb'))}` },\n                timeout: 60000,\n            });\n            results.push({\n                index: photo.index,\n                title: photo.title,\n                photo_id: photo.photoId,\n                image_url: photo.imageUrl,\n                detail_url: photo.detailUrl,\n                status: result.success ? 'success' : 'failed',","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douban/download.js#L26-L62","documentation":"In the douban download command, when a specific photo id is requested the fetched photo list is filtered by photoId; if nothing matches, EmptyResultError is thrown because there is nothing to download. The message hints at listing photos as JSON first to discover valid ids.","triggerScenarios":"Typo in the photo id; the photo belongs to a different subject; the photo was deleted; the load only fetched the first page(s) (default limit 120) so an older photo beyond the fetched window is absent from data.photos.","commonSituations":"Hardcoded/stale photo ids from an earlier run; pagination limits hiding older photos; confusing photo ids across two similar subjects.","solutions":["Run \"douban photos <subjectId> -f json\" and pick a photoId that actually exists","Increase the load limit or paginate further if the photo may be beyond the fetched set","Confirm the photo belongs to the given subject and has not been deleted"],"exampleFix":"// before\ndouban download 1234567 --photo 99999999\n// after\ndouban photos 1234567 -f json   # find valid photoId\ndouban download 1234567 --photo <validId>","handlingStrategy":"fallback","validationCode":"const listing = await doubanPhotos(subjectId, { format: 'json' });\nconst valid = listing.photos.some(p => p.photoId === requestedPhotoId);\nif (!valid) console.error(`Photo ${requestedPhotoId} not in subject ${subjectId}; pick from -f json output`);","typeGuard":"function photoExists(photos, id) { return photos.some(p => p.photoId === id); }","tryCatchPattern":"try { await doubanDownload(subjectId, { photo: requestedPhotoId }); } catch (e) { if (e instanceof EmptyResultError) { const all = await doubanPhotos(subjectId, { format: 'json' }); /* pick a valid photoId */ } else throw e; }","preventionTips":["Always list photos as JSON before downloading a specific id","Raise the load limit / paginate if the target photo may be older than the fetched window","Validate photo ids belong to the subject before batching"],"tags":["empty-result","douban","not-found","pagination"],"backgroundTag":"resource-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}