(e: unknown)
| 194 | * (disk full, permission denied) before flipping the git-fallback kill switch. |
| 195 | */ |
| 196 | export function classifyGcsError(e: unknown): string { |
| 197 | if (axios.isAxiosError(e)) { |
| 198 | if (e.code === 'ECONNABORTED') return 'timeout' |
| 199 | if (e.response) return `http_${e.response.status}` |
| 200 | return 'network' |
| 201 | } |
| 202 | const code = getErrnoCode(e) |
| 203 | // Node fs errno codes are E<UPPERCASE> (ENOSPC, EACCES). Axios also sets |
| 204 | // .code (ERR_NETWORK, ERR_BAD_OPTION, EPROTO) — don't bucket those as fs. |
| 205 | if (code && /^E[A-Z]+$/.test(code) && !code.startsWith('ERR_')) { |
| 206 | return KNOWN_FS_CODES.has(code) ? `fs_${code}` : 'fs_other' |
| 207 | } |
| 208 | // fflate sets numeric .code (0-14) on inflate/unzip errors — catches |
| 209 | // deflate-level corruption ("unexpected EOF", "invalid block type") that |
| 210 | // the message regex misses. |
| 211 | if (typeof (e as { code?: unknown })?.code === 'number') return 'zip_parse' |
| 212 | const msg = errorMessage(e) |
| 213 | if (/unzip|invalid zip|central directory/i.test(msg)) return 'zip_parse' |
| 214 | if (/empty body/.test(msg)) return 'empty_latest' |
| 215 | return 'other' |
| 216 | } |
| 217 |
no test coverage detected