{"record":{"id":"eda8a57ece4b63d7","repo":"odysseus-dev/odysseus","slug":"found-data-grounding-label-object-but-sam","errorCode":null,"errorMessage":"Found ${data.grounding.label || 'object'}, but SAM returned an empty mask","messagePattern":"Found (.+?), but SAM returned an empty mask","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"static/js/galleryEditor.js","lineNumber":2011,"sourceCode":"  }\n}\n\nasync function _requestAndApplySamMask(layer, payload, mode, seedPoint, opts = {}) {\n  const res = await fetch('/api/image/mask', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      signal: opts.signal,\n      body: JSON.stringify({\n        image: layer.canvas.toDataURL('image/png').split(',')[1],\n        ...payload,\n      }),\n    });\n  const data = await res.json().catch(() => ({}));\n  if (!res.ok || !data.mask) {\n    throw new Error(data.detail || data.error || `Mask failed (${res.status})`);\n  }\n  if (!data.bbox) {\n    throw new Error(data.grounding ? `Found ${data.grounding.label || 'object'}, but SAM returned an empty mask` : 'SAM returned an empty mask');\n  }\n\n  const img = new Image();\n  await new Promise((resolve, reject) => {\n    img.onload = resolve;\n    img.onerror = () => reject(new Error('Failed to decode mask'));\n    img.src = 'data:image/png;base64,' + data.mask;\n  });\n  const mask = document.createElement('canvas');\n  mask.width = layer.canvas.width;\n  mask.height = layer.canvas.height;\n  const mctx = mask.getContext('2d');\n  mctx.drawImage(img, 0, 0, mask.width, mask.height);\n  const maskData = mctx.getImageData(0, 0, mask.width, mask.height);\n  const md = maskData.data;\n  for (let i = 0; i < md.length; i += 4) {\n    const alpha = md[i]; // server mask is white selected / black unselected\n    md[i] = 255;","sourceCodeStart":1993,"sourceCodeEnd":2029,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/galleryEditor.js#L1993-L2029","documentation":"Thrown when the mask endpoint succeeds and returns a mask but no bbox, meaning SAM produced an empty/zero-area segmentation. If grounding metadata is present the message names the detected label ('Found dog, but SAM returned an empty mask'); otherwise it is the bare 'SAM returned an empty mask'. This is a model-quality failure, not a transport error.","triggerScenarios":"Clicking on a point/region where the grounding model detects a label but SAM cannot segment it at that pixel (low contrast, tiny object, ambiguous click); prompting on flat backgrounds like sky or walls; resolution mismatch between the source image SAM saw and the canvas.","commonSituations":"Users clicking imprecisely on object edges; photos with heavy motion blur or low contrast; a grounding hit on a coarse label ('object') with no real instance under the cursor.","solutions":["Click again closer to the center of the target object.","Try a box/rect selection mode instead of a single seed point.","Zoom in and retry on a well-contrasted part of the target.","If it always fails, verify the SAM checkpoint version matches what the server expects."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!data.mask || !data.bbox || data.bbox.every(v => v === 0)) { throw new Error('SAM returned an empty mask — retry with a different click'); }","typeGuard":null,"tryCatchPattern":"catch (e) { if (/empty mask/i.test(e.message)) { toast('Click closer to the center of the object and retry'); return; } throw e; }","preventionTips":["Validate bbox is non-degenerate before building the mask canvas","Treat empty masks as retryable UX guidance, not hard failures","Prefer box prompts over single seed points on low-contrast subjects"],"tags":["sam","segmentation","model-quality","editor"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}