{"record":{"id":"94e7cd88b609f0ea","repo":"odysseus-dev/odysseus","slug":"sam-mask-failed-exc","errorCode":null,"errorMessage":"SAM mask failed: {exc}","messagePattern":"SAM mask failed: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/gallery/gallery_routes.py","lineNumber":1961,"sourceCode":"\n            mask_img = Image.fromarray(mask_array, mode=\"L\")\n            if mask_img.size != image.size:\n                mask_img = mask_img.resize(image.size, Image.NEAREST)\n            bbox = mask_img.getbbox()\n            result = {\n                \"mask\": _pil_image_to_b64(mask_img),\n                \"bbox\": list(bbox) if bbox else None,\n                \"model\": backend[\"model_id\"],\n                \"device\": device,\n            }\n            if grounded:\n                result[\"grounding\"] = grounded\n            return result\n        except HTTPException:\n            raise\n        except Exception as exc:\n            logger.exception(\"smart_mask failed\")\n            raise HTTPException(500, f\"SAM mask failed: {exc}\") from exc\n\n    @router.post(\"/api/image/remove-bg\")\n    async def remove_background(request: Request):\n        \"\"\"Remove background from an image. If the client passes a `hint_mask`\n        (white-where-the-user-wants-the-subject PNG, same dims as the\n        image), we constrain the output:\n\n          1. Crop the image to the mask's bounding box (with padding) so\n             the model only sees the region the user cares about.\n          2. Run rembg on that crop.\n          3. Paste the result back at the original offset.\n          4. Multiply the final alpha by the user's mask, so anything\n             outside the hint becomes transparent regardless of what the\n             model thought was foreground.\n        \"\"\"\n        require_privilege(request, \"can_generate_images\")\n        body = await request.json()\n        image_b64 = body.get(\"image\")","sourceCodeStart":1943,"sourceCodeEnd":1979,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/gallery/gallery_routes.py#L1943-L1979","documentation":"HTTP 500 catch-all for the SAM smart-mask endpoint: any exception other than an HTTPException during processor encoding, model inference (torch), post_process_masks, or mask-to-PIL conversion is logged with logger.exception and re-raised as 'SAM mask failed: {exc}'. The chained exception preserves the root cause.","triggerScenarios":"POST a valid prompt when CUDA OOMs during model(**model_inputs), transformers version incompatibility in post_process_masks, corrupt image that PIL opens but processor rejects, or model weights failing to download mid-request.","commonSituations":"Large images on a small GPU (OOM); mismatched transformers/torch versions after an upgrade; first-request model download interrupted by a proxy; device mismatch after toggling CUDA availability.","solutions":["Read the server log's 'smart_mask failed' traceback — the message suffix {exc} plus the stack identify the true failure","For CUDA OOM, downscale the image before sending or free GPU memory / use the CPU device","Pin compatible torch/transformers versions per requirements.txt and re-download model weights","Retry once after verifying the image decodes locally with PIL"],"exampleFix":"# client: surface the server's chained cause\nresp = requests.post(url, json=payload)\nif resp.status_code == 500 and 'SAM mask failed' in resp.text:\n    logging.error('server traceback: %s', resp.text)","handlingStrategy":"try-catch","validationCode":"# pre-flight the pipeline's fragile assumptions\nfrom PIL import Image\nimg = Image.open(io.BytesIO(base64.b64decode(b64)))\nimg.convert('RGB').save(io.BytesIO(), format='PNG')  # decodable check\nif img.width * img.height > 4_000_000:\n    img.thumbnail((2048, 2048))  # reduce CUDA OOM risk","typeGuard":null,"tryCatchPattern":"try:\n    result = smart_mask(image=b64, points=pts)\nexcept HTTPException as e:\n    if e.status_code == 500:\n        logger.exception('SAM failed; server log has full traceback')\n        if 'out of memory' in str(e.detail):\n            result = smart_mask(image=downscaled_b64, points=pts)  # one retry, smaller\n        else:\n            raise","preventionTips":["Downscale large images before sending to bound GPU memory","Pre-warm/download model weights at server startup so first-request downloads can't fail mid-inference","Pin torch/transformers versions in requirements and test the SAM route in CI against them"],"tags":["http-500","sam","pytorch","catch-all","segmentation"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}