ruvnet/ruflo · error · Error

Pinata pin failed (HTTP ${res.status}): ${JSON.stringify(res

Error message

Pinata pin failed (HTTP ${res.status}): ${JSON.stringify(res.data)}

What it means

Thrown by RvfaPublisher.pin() when Pinata's POST /pinning/pinByHash returns a non-200 status. pin() asks Pinata to pin an existing CID by hash. The error embeds status and body for diagnosis.

Source

Thrown at v3/@claude-flow/cli/src/appliance/rvfa-distribution.ts:391

  async list(): Promise<PublishedItem[]> {
    const res = await pinataReq('GET', `${this.api}/data/pinList?status=pinned&pageLimit=100`, this.jwt);
    if (res.status !== 200)
      throw new Error(`Pinata list failed (HTTP ${res.status}): ${JSON.stringify(res.data)}`);
    const d = res.data as { rows: Array<{
      ipfs_pin_hash: string; metadata?: { name?: string }; size: number; date_pinned: string;
    }> };
    return (d.rows || []).map((r) => ({
      cid: r.ipfs_pin_hash, name: r.metadata?.name || r.ipfs_pin_hash,
      size: r.size, date: r.date_pinned,
    }));
  }

  async pin(cid: string, name?: string): Promise<void> {
    const body = Buffer.from(JSON.stringify({ hashToPin: cid, pinataMetadata: { name: name || cid } }));
    const res = await pinataReq('POST', `${this.api}/pinning/pinByHash`, this.jwt, body, 'application/json');
    if (res.status !== 200)
      throw new Error(`Pinata pin failed (HTTP ${res.status}): ${JSON.stringify(res.data)}`);
  }
}

// ── Convenience exports ──────────────────────────────────────
export function createPublisher(config?: Partial<PublishConfig>): RvfaPublisher {
  return new RvfaPublisher({ pinataJwt: config?.pinataJwt, gatewayUrl: config?.gatewayUrl, apiUrl: config?.apiUrl });
}

export async function createAndVerifyPatch(
  options: CreatePatchOptions,
): Promise<{ patch: Buffer; verification: PatchVerifyResult }> {
  const patch = await RvfaPatcher.createPatch(options);
  const verification = await RvfaPatcher.verifyPatch(patch);
  return { patch, verification };
}

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Validate the CID format (multibase, expected length for the hash) before calling pin().
  2. For 401/403: ensure the JWT has pinByHash/pinning permissions.
  3. For 409/duplicate: treat as success if the CID is already pinned (call list() to confirm).
  4. For 429/5xx: retry with backoff.
Defensive patterns

Strategy: validation

Validate before calling

function isValidCid(cid: string): boolean {
  // Basic multibase/base58 or base32 CID check (v0 starts with 'Qm', v1 with 'b')
  return /^(Qm[1-9A-HJ-NP-Za-km-z]{44}|b[a-z2-7]{52,})$/.test(cid);
}

Try / catch

try {
  await pub.pin(cid, name);
} catch (e) {
  const msg = (e as Error).message;
  if (/HTTP 400/.test(msg)) throw new Error(`CID "${cid}" is malformed or unknown to Pinata`);
  if (/HTTP 409|already pinned/i.test(msg)) return; // idempotent success
  throw e;
}

Prevention

When it happens

Trigger: HTTP 400 (malformed or invalid-length CID), 401/403 (JWT lacks pinning scope), 409 (already pinned — Pinata sometimes returns non-200 for duplicates), 429 (rate limit), or 5xx.

Common situations: Passing a CID with the wrong length or base; the CID exists on a gateway Pinata cannot reach; the JWT was scoped to uploads only; duplicate pin attempts during a retry loop.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/508e584c6332167c. Report an issue: GitHub.