antiwork/gumroad · error · ResponseError

${responseData.error}

Error message

${responseData.error}

What it means

createThumbnail (app/javascript/data/thumbnails.ts:9-28) POSTs a signed Active Storage blob id to Routes.link_thumbnails_path and throws ResponseError(responseData.error) when the server answers a parsed body with success:false. This is an application-level rejection: the HTTP exchange completed, but the Rails controller refused to attach the thumbnail and returned its reason in the error string — typically an invalid/expired signed_blob_id, a rejected content type, or a product the seller cannot edit.

Source

Thrown at app/javascript/data/thumbnails.ts:24

export type ThumbnailPayload = { type: "file"; signedBlobId: string };

export const createThumbnail = async (permalink: string, thumbnailPayload: ThumbnailPayload): Promise<Thumbnail> => {
  const response = await request({
    method: "POST",
    accept: "json",
    url: Routes.link_thumbnails_path(permalink),
    data: {
      thumbnail: { signed_blob_id: thumbnailPayload.signedBlobId },
    },
  });

  if (response.ok) {
    const responseData = typia.assert<{ success: true; thumbnail: Thumbnail } | { success: false; error: string }>(
      await response.json(),
    );
    if (responseData.success) return responseData.thumbnail;
    throw new ResponseError(responseData.error);
  }

  throw new ResponseError();
};

export const deleteThumbnail = async (permalink: string, thumbnailId: string) => {
  const response = await request({
    method: "DELETE",
    accept: "json",
    url: Routes.link_thumbnail_path(permalink, thumbnailId),
  });

  if (response.ok) {
    const responseData = typia.assert<{ success: true; thumbnail: Thumbnail } | { success: false }>(
      await response.json(),
    );
    if (responseData.success) return;
  }

View on GitHub (pinned to afeacbd394)

Solutions

  1. Retry the full upload flow — re-run the direct upload so signedBlobId is freshly minted, then POST again
  2. Verify the direct upload succeeded before calling createThumbnail (check the upload step's own result, not just form state)
  3. Confirm the file is an accepted image type and under the size limit the controller enforces
  4. Log/inspect responseData.error — the server states the exact refusal (invalid blob, wrong type, not permitted)
  5. Ensure the acting seller owns the product identified by permalink

Example fix

// components/ProductEdit/ProductTab/ThumbnailEditor.tsx — only attach a blob that finished uploading
const upload = await directUpload(file); // throws on its own failure
if (!upload.signedBlobId) throw new Error("Upload did not complete");
const thumbnail = await createThumbnail(permalink, { type: "file", signedBlobId: upload.signedBlobId });
Defensive patterns

Strategy: validation

Validate before calling

const canAttach = (p: ThumbnailPayload) =>
  typeof p.signedBlobId === "string" && p.signedBlobId.length > 0; // never POST an upload that didn't finish
if (!canAttach(thumbnailPayload)) throw new Error("Upload did not complete");

Type guard

import typia from "typia";
const isThumbnailResponse = typia.is<{ success: true; thumbnail: Thumbnail } | { success: false; error: string }>;

Try / catch

try {
  return await createThumbnail(permalink, payload);
} catch (e) {
  assertResponseError(e);
  showError(e.message); // server's refusal: invalid signed_blob_id, bad type, no permission
}

Prevention

When it happens

Trigger: Uploading a product thumbnail in ProductEdit/ProductTab/ThumbnailEditor.tsx:62 when the direct-upload blob never actually uploaded (signedBlobId references nothing); the signed id expired before the POST; the file type/size is rejected server-side; the permalink belongs to a product not owned by the acting seller.

Common situations: Direct upload to Active Storage silently failing (network drop between S3 upload and attach step); reusing a signed_blob_id captured in an old form state after the upload was redone; tests posting fixture signed ids that were never generated; product permissions changed while the edit page was open.

Related errors


AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21). Data as JSON: /api/errors/48d8ece6a2f7c87c. Report an issue: GitHub.