{"record":{"id":"ffc70f45c7b7dee1","repo":"jackwener/OpenCLI","slug":"archive-item-returned-malformed-payload-files-mus","errorCode":null,"errorMessage":"archive item returned malformed payload: files must be an array","messagePattern":"archive item returned malformed payload: files must be an array","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/archive/item.js","lineNumber":77,"sourceCode":"\n        const meta = data?.metadata;\n        // The metadata endpoint returns {} for missing or dark items.\n        if (!meta || typeof meta !== 'object' || !meta.identifier) {\n            throw new EmptyResultError('archive item', `No public metadata for \"${identifier}\" on archive.org.`);\n        }\n        const responseIdentifier = String(meta.identifier);\n        if (!IDENTIFIER_RE.test(responseIdentifier)) {\n            throw new CommandExecutionError('archive item returned malformed payload: metadata.identifier is not stable');\n        }\n        if (responseIdentifier !== identifier) {\n            throw new CommandExecutionError(`archive item returned metadata for \"${responseIdentifier}\" instead of \"${identifier}\"`);\n        }\n\n        const creator = Array.isArray(meta.creator) ? meta.creator.join(', ') : String(meta.creator ?? '');\n        const collection = Array.isArray(meta.collection) ? meta.collection.join(', ') : String(meta.collection ?? '');\n        const description = Array.isArray(meta.description) ? meta.description.join(' ') : String(meta.description ?? '');\n        if (!Array.isArray(data.files)) {\n            throw new CommandExecutionError('archive item returned malformed payload: files must be an array');\n        }\n\n        return [{\n            identifier: responseIdentifier,\n            title: String(meta.title ?? ''),\n            creator,\n            date: meta.date ? String(meta.date).slice(0, 10) : '',\n            mediatype: String(meta.mediatype ?? ''),\n            collection,\n            description,\n            file_count: data.files.length,\n            url: `https://archive.org/details/${responseIdentifier}`,\n        }];\n    },\n});\n","sourceCodeStart":59,"sourceCodeEnd":93,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/archive/item.js#L59-L93","documentation":"This CommandExecutionError is thrown by the `archive item` command when the archive.org /metadata/<identifier> endpoint returns a JSON payload whose `files` field is not an array. The CLI normalizes metadata fields (creator, collection, description) and expects `data.files` to be an array so it can count files; anything else (missing, null, object, string) is treated as a malformed response. It indicates the remote API returned an unexpected shape rather than a caller input mistake.","triggerScenarios":"Calling `opencli archive item <identifier>` where the archive.org metadata response lacks a `files` array — e.g. the item is a collection or dark item, archive.org serves a partial/degraded metadata document, a proxy/CDN returns an HTML error page with 200, or the item schema changed.","commonSituations":"Fetching metadata for collection-type identifiers where `files` is absent; archive.org serving truncated responses during incidents; corporate proxies intercepting the request and returning non-JSON/odd JSON; scraping very old or unusual items whose metadata never had a files list.","solutions":["Retry the command — transient archive.org partial responses often resolve on retry","Verify the identifier points to a concrete item (not a collection) that publicly lists files","Inspect the raw payload with `curl https://archive.org/metadata/<identifier>` to confirm the `files` field shape","If the item legitimately has no files list, treat the item as empty rather than malformed and/or catch CommandExecutionError and fall back to metadata-only output"],"exampleFix":"// before\nconst rows = await run(['archive', 'item', identifier]);\n// after\nlet rows;\ntry {\n    rows = await run(['archive', 'item', identifier]);\n} catch (err) {\n    if (/files must be an array/.test(String(err?.message))) {\n        rows = []; // item exposes no files list; proceed with metadata only\n    } else {\n        throw err;\n    }\n}","handlingStrategy":"type-guard","validationCode":"const meta = await fetchMetadata(identifier);\nif (!Array.isArray(meta?.files)) {\n    console.warn(`item ${identifier} has no files array; skipping`);\n}","typeGuard":"function hasFilesArray(data) {\n    return Boolean(data) && typeof data === 'object' && Array.isArray(data.files);\n}","tryCatchPattern":"try {\n    rows = await run(['archive', 'item', id]);\n} catch (err) {\n    if (err instanceof CommandExecutionError && err.message.includes('files must be an array')) {\n        rows = []; // treat as file-less item\n    } else { throw err; }\n}","preventionTips":["Validate identifiers refer to concrete items, not collections","Spot-check the raw /metadata/<id> payload for new item types you ingest","Build a response-shape assertion helper and run it over sampled items","Add retry/fallback logic for archive.org partial responses"],"tags":["api","payload-validation","archive-org","cli"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}