{"record":{"id":"8457aea5d6b471c7","repo":"ruvnet/ruflo","slug":"pinata-upload-failed-response-status-error","errorCode":null,"errorMessage":"Pinata upload failed: ${response.status} ${error}","messagePattern":"Pinata upload failed: (.+?) (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/transfer/ipfs/upload.ts","lineNumber":180,"sourceCode":"    Buffer.from(`Content-Disposition: form-data; name=\"file\"; filename=\"${name}\"\\r\\n`),\n    Buffer.from(`Content-Type: application/json\\r\\n\\r\\n`),\n    content,\n    Buffer.from(`\\r\\n--${boundary}--\\r\\n`),\n  ]);\n\n  const response = await fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', {\n    method: 'POST',\n    headers: {\n      'pinata_api_key': apiKey,\n      'pinata_secret_api_key': apiSecret,\n      'Content-Type': `multipart/form-data; boundary=${boundary}`,\n    },\n    body,\n  });\n\n  if (!response.ok) {\n    const error = await response.text();\n    throw new Error(`Pinata upload failed: ${response.status} ${error}`);\n  }\n\n  const result = await response.json() as { IpfsHash: string; PinSize: number };\n  const cid = result.IpfsHash;\n  const gateway = options.gateway || 'https://gateway.pinata.cloud';\n\n  console.log(`[IPFS] Upload complete!`);\n  console.log(`[IPFS] CID: ${cid}`);\n\n  return {\n    cid,\n    size: content.length,\n    gateway,\n    pinnedAt: new Date().toISOString(),\n    url: `${gateway}/ipfs/${cid}`,\n  };\n}\n","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/transfer/ipfs/upload.ts#L162-L198","documentation":"uploadToPinata() POSTs the multipart body to https://api.pinata.cloud/pinning/pinFileToIPFS with pinata_api_key / pinata_secret_api_key headers (the legacy v1 key scheme). A non-ok response is converted into this error carrying the status and Pinata's response text. Typical statuses: 401 invalid/expired key pair, 403 key not authorized for pinning, 429 rate/quota limits, 5xx Pinata outages.","triggerScenarios":"Uploading with a revoked or mistyped PINATA_API_KEY/SECRET pair; hitting free-tier rate limits during bulk pattern transfers; using legacy v1 API keys on endpoints that now demand JWT signatures; transient Pinata 5xx.","commonSituations":"Regenerated keys in the Pinata console but stale env vars still loaded; bulk uploads of many CFP files tripping 429; corporate egress filtering returning 403; legacy key auth deprecated in favor of Pinata JWT gateways.","solutions":["Match the status: 401/403 -> regenerate the key pair in the Pinata console and update both env vars; 429 -> back off and retry with fewer concurrent uploads","Verify the pair is consistent (old key with new secret fails as 401)","For 5xx, retry with exponential backoff — Pinata outages are usually brief","Fall back to another configured provider (web3.storage token or local IPFS_API_URL node) if Pinata keeps rejecting"],"exampleFix":"// before\nawait uploadToIPFS(content, { provider: 'pinata' }); // 401 -> Pinata upload failed: 401 ...\n\n// after\nconst isRetryable = (msg: string) => /Pinata upload failed: (429|5\\d\\d)/.test(msg);\nfor (let attempt = 0; ; attempt++) {\n  try { return await uploadToIPFS(content, { provider: 'pinata' }); }\n  catch (e) {\n    if (attempt < 3 && isRetryable(String(e?.message))) {\n      await new Promise(r => setTimeout(r, 2 ** attempt * 1000)); continue;\n    }\n    throw e;\n  }\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"const isPinataFailure = (e: unknown) => /Pinata upload failed: (\\d+)/.test(String((e as Error)?.message));\n\ntry {\n  result = await uploadToIPFS(content, { provider: 'pinata' });\n} catch (e) {\n  if (!isPinataFailure(e)) throw e;\n  const status = Number(/: (\\d+)/.exec(String((e as Error).message))?.[1]);\n  if (status === 429 || status >= 500) {\n    await new Promise(r => setTimeout(r, 2 ** attempt * 1000)); // retryable\n    continue;\n  }\n  result = await uploadToIPFS(content, { provider: 'web3' }); // 401/403: fall back, then fix keys","preventionTips":["Parse the HTTP status out of the message to route 429/5xx to retry and 401/403 to credential rotation","Throttle bulk uploads to stay under Pinata rate limits instead of reacting to 429s","Keep a fallback provider configured so a single pinning outage does not stop transfers"],"tags":["ipfs","pinata","http","upload","api-error","rate-limit"],"backgroundTag":"http-request-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}