{"record":{"id":"ac96e1f1d8e83e52","repo":"ruvnet/ruflo","slug":"pinata-upload-failed-http-res-status-json","errorCode":null,"errorMessage":"Pinata upload failed (HTTP ${res.status}): ${JSON.stringify(res.data)}","messagePattern":"Pinata upload failed \\(HTTP (.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/appliance/rvfa-distribution.ts","lineNumber":344,"sourceCode":"  private jwt: string;\n  private gw: string;\n  private api: string;\n\n  constructor(config: PublishConfig) {\n    this.jwt = config.pinataJwt || process.env.PINATA_API_JWT || '';\n    this.gw = (config.gatewayUrl || DEFAULT_GW).replace(/\\/+$/, '');\n    this.api = (config.apiUrl || DEFAULT_API).replace(/\\/+$/, '');\n    if (!this.jwt) throw new Error('Pinata JWT required (config.pinataJwt or PINATA_API_JWT)');\n  }\n\n  private async upload(\n    fileName: string, data: Buffer, kv: Record<string, string>,\n  ): Promise<PublishResult> {\n    const meta = JSON.stringify({ name: fileName, keyvalues: kv });\n    const { body, ct } = multipart('file', fileName, data, meta);\n    const res = await pinataReq('POST', `${this.api}/pinning/pinFileToIPFS`, this.jwt, body, ct);\n    if (res.status !== 200)\n      throw new Error(`Pinata upload failed (HTTP ${res.status}): ${JSON.stringify(res.data)}`);\n    const r = res.data as { IpfsHash: string; PinSize: number };\n    return {\n      cid: r.IpfsHash, size: r.PinSize,\n      gatewayUrl: `${this.gw}/ipfs/${r.IpfsHash}`, pinataUrl: `${this.api}/pinning/pins/${r.IpfsHash}`,\n    };\n  }\n\n  async publish(rvfaPath: string, meta?: PublishMetadata): Promise<PublishResult> {\n    const data = await readFile(rvfaPath);\n    const name = meta?.name || rvfaPath.split('/').pop() || 'appliance.rvf';\n    return this.upload(name, data, {\n      type: 'rvfa-appliance', version: meta?.version || '',\n      profile: meta?.profile || '', description: meta?.description || '',\n    });\n  }\n\n  async publishPatch(patchBuf: Buffer, meta?: PublishMetadata): Promise<PublishResult> {\n    const name = meta?.name || `patch-${Date.now()}.rvfp`;","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/appliance/rvfa-distribution.ts#L326-L362","documentation":"The POST to Pinata's /pinning/pinFileToIPFS returned a non-200 status; the message embeds both the code and Pinata's response body, so the exact cause is in the text. 401 means missing/invalid JWT, 402/403 plan or pin limits, 400 malformed multipart/oversized file, 429/5xx transient server-side conditions.","triggerScenarios":"`publisher.publish(rvfaPath)` or the internal `upload()` with an expired/revoked JWT (401); free-plan pin quota exhausted (402/403); payload exceeding Pinata's upload limit or malformed metadata (400); Pinata incident (5xx).","commonSituations":"JWT rotated since deployment; enough artifacts pinned to hit plan caps; a custom apiUrl pointing at a deprecated endpoint; transient 5xx during publishing runs.","solutions":["Read the embedded status: 401 → regenerate the JWT at pinata.cloud and update PINATA_API_JWT/config","402/403 → check plan and pin limits in the Pinata console; unpin old appliance versions to free slots","429/5xx → retry with exponential backoff; check status.pinata.cloud","400 → inspect the JSON body in the message; verify file size limits and the multipart metadata"],"exampleFix":"// before\nconst result = await publisher.publish(rvfaPath); // throws on first non-200\n\n// after\ntry {\n  await publisher.publish(rvfaPath);\n} catch (e) {\n  if (/HTTP 401/.test(String(e))) { /* refresh PINATA_API_JWT, then retry once */ }\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"if (!process.env.PINATA_API_JWT) throw new Error('PINATA_API_JWT missing');\nconst st = await stat(rvfaPath);\nif (st.size > 100 * 1024 * 1024) {\n  throw new Error('File exceeds Pinata upload limit — split or compress it');\n}","typeGuard":null,"tryCatchPattern":"async function publishWithRetry(p: RvfaPublisher, path: string, tries = 3) {\n  for (let attempt = 0; ; attempt++) {\n    try {\n      return await p.publish(path);\n    } catch (e) {\n      const msg = String(e);\n      if (/HTTP (429|5\\d\\d)/.test(msg) && attempt < tries - 1) {\n        await new Promise(r => setTimeout(r, 2 ** attempt * 1000)); // backoff, retry transient only\n        continue;\n      }\n      throw e; // 401/402/400 are configuration or payload errors — do not retry\n    }\n  }\n}","preventionTips":["Alert on 401 responses so JWT rotation never silently breaks publishing","Retry only 429/5xx with exponential backoff; treat 401/402/400 as fatal config/payload errors","Keep pinned-content volume under the plan limit by unpinning old appliance versions"],"tags":["rvfa-distribution","pinata","ipfs","http","publish"],"backgroundTag":"http-api-error","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}