ruvnet/ruflo · error · Error
Pinata upload failed (HTTP ${res.status}): ${JSON.stringify(
Error message
Pinata upload failed (HTTP ${res.status}): ${JSON.stringify(res.data)} What it means
Thrown by RvfaPublisher.upload() when Pinata's POST /pinning/pinFileToIPFS returns a non-200 status. The error embeds both the HTTP status and the parsed response body (JSON or raw) so the caller can diagnose the upstream cause. Upload is the backing call for publish() and publishPatch().
Source
Thrown at v3/@claude-flow/cli/src/appliance/rvfa-distribution.ts:344
private jwt: string;
private gw: string;
private api: string;
constructor(config: PublishConfig) {
this.jwt = config.pinataJwt || process.env.PINATA_API_JWT || '';
this.gw = (config.gatewayUrl || DEFAULT_GW).replace(/\/+$/, '');
this.api = (config.apiUrl || DEFAULT_API).replace(/\/+$/, '');
if (!this.jwt) throw new Error('Pinata JWT required (config.pinataJwt or PINATA_API_JWT)');
}
private async upload(
fileName: string, data: Buffer, kv: Record<string, string>,
): Promise<PublishResult> {
const meta = JSON.stringify({ name: fileName, keyvalues: kv });
const { body, ct } = multipart('file', fileName, data, meta);
const res = await pinataReq('POST', `${this.api}/pinning/pinFileToIPFS`, this.jwt, body, ct);
if (res.status !== 200)
throw new Error(`Pinata upload failed (HTTP ${res.status}): ${JSON.stringify(res.data)}`);
const r = res.data as { IpfsHash: string; PinSize: number };
return {
cid: r.IpfsHash, size: r.PinSize,
gatewayUrl: `${this.gw}/ipfs/${r.IpfsHash}`, pinataUrl: `${this.api}/pinning/pins/${r.IpfsHash}`,
};
}
async publish(rvfaPath: string, meta?: PublishMetadata): Promise<PublishResult> {
const data = await readFile(rvfaPath);
const name = meta?.name || rvfaPath.split('/').pop() || 'appliance.rvf';
return this.upload(name, data, {
type: 'rvfa-appliance', version: meta?.version || '',
profile: meta?.profile || '', description: meta?.description || '',
});
}
async publishPatch(patchBuf: Buffer, meta?: PublishMetadata): Promise<PublishResult> {
const name = meta?.name || `patch-${Date.now()}.rvfp`;View on GitHub (pinned to 6b01dc5a68)
Solutions
- Inspect the embedded status code and JSON body in the error message to identify the cause.
- For 401/403: regenerate the JWT from the Pinata dashboard and ensure it has pinning permissions.
- For 413: reduce appliance size (strip sections) or upgrade the Pinata plan.
- For 429/5xx: retry with exponential backoff (the call is idempotent on distinct filenames).
Defensive patterns
Strategy: retry
Try / catch
async function safePublish(pub: RvfaPublisher, path: string, meta?: PublishMetadata) {
const maxAttempts = 4;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await pub.publish(path, meta);
} catch (e) {
const msg = (e as Error).message;
const transient = /HTTP 429|HTTP 5\d\d/.test(msg);
if (!transient || attempt === maxAttempts) throw e;
await new Promise(r => setTimeout(r, 500 * 2 ** attempt));
}
}
throw new Error('unreachable');
} Prevention
- Inspect the embedded HTTP status code in the message before retrying — only retry 429/5xx.
- Keep the JWT fresh; rotate before expiry to avoid 401 cascades.
- Check appliance size against the Pinata plan's per-file limit before uploading.
- Throttle concurrent publishes to avoid 429 rate limiting.
When it happens
Trigger: HTTP 401 (invalid/expired JWT), 403 (JWT lacks pinning scope), 413 (file exceeds Pinata plan size limit), 429 (rate limited), 5xx (Pinata outage), or 400 (malformed multipart body).
Common situations: JWT expired since the publisher was constructed; appliance file is larger than the Pinata plan's per-file cap; burst publishing trips the rate limiter; transient Pinata 5xx during a release window.
Related errors
- Pinata list failed (HTTP ${res.status}): ${JSON.stringify(re
- Pinata pin failed (HTTP ${res.status}): ${JSON.stringify(res
- Pinata JWT required (config.pinataJwt or PINATA_API_JWT)
- SSRF guard: private/loopback host rejected — ${host}
- SSRF guard: private/loopback host rejected — ${host}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/ac96e1f1d8e83e52.
Report an issue: GitHub.