Bug: /v1/digitize 500s when input SVG uses CSS named colors ('white', 'black', etc.) — color not normalized before StitchObject validation #19
Labels
No labels
blocked:needs-attention
blocked:needs-decision
bug
feature-request
in-progress
needs-decompose
quickfix
triage-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/easystitch-service#19
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
POST /v1/digitizereturns HTTP 500 withinvalid hex color: 'white'when the input SVG (or any raster converted from such an SVG) uses a CSS named color likefill="white"instead of a#RRGGBBhex literal.Steps to Reproduce
/v1/digitize:image(the SVG,image/svg+xml) +settings(JSON; e.g.{"hoop_size_mm":{"w":100,"h":100}}).{"error":"internal","request_id":"<id>"}.Expected
thread_colorshould accept CSS named colors (white, black, red, blue, …) and either normalize them to#RRGGBBat the engine→service boundary, OR reject them with a 422 with a clear message about using hex colors. The current behavior is a 500 with no actionable feedback.Actual
Server traceback (from
/tmp/easystitch-health.log):Root Cause
_validate_hexinmodels.py:26only accepts strings shaped#RRGGBB._flushinpipeline_runner.py:163-175constructs aServiceStitchObjectwiththread_color=current_color or "#000000"— theorfallback only fires whencurrent_coloris falsy (None/"").'white'is truthy, so the invalid string is passed straight to pydantic, which 500s.The engine (src/main_pipeline) emits stitches with
colorset to whatever string the SVG/raster color came back as. CSS named colors (white,black,red, etc.) and possiblyrgb(...)/rgba(...)forms flow through unchecked.Suggested Fix
Pick one (in order of preference):
_group_stitches_into_objects(pipeline_runner.py:163-175) before constructingServiceStitchObject. A smallcss_named_to_hex()helper plus handling forrgb(...)would make the pipeline self-healing and keep the API contract strict.models.py:103-106to accept named colors by calling a shared normalizer. Risk: changes the public API contract in a potentially breaking way (callers may rely on strict hex).Environment
easystitch-servicerunning onlocalhost:8080(PID 1457239, started 2026-08-29)fill="white"andfill="black"→ 500#FFFFFF/#000000→ digitize returns 200 withtotalStitches: 6572, colorCount: 2butregions: []andstitchObjects: [](separately, may be related — see follow-up notes)Severity
High for any non-power-user who uploads SVGs from design tools — most design exports use named colors. The 500 + opaque request_id also obscures the cause from the GUI; the user sees a generic error and has no recourse.
Shakedown Context
This came out of the BB-monogram GUI shakedown on 2026-09-01. Real artifact at
/tmp/shakedown-bb/bb.svg. Request_id captured for the failing call:c08a44f1230e(and32d35034798don a repeat).Fixed and pushed to main (commit
530920f).Fix: Normalize at the engine→service boundary in
_group_stitches_into_objects(pipeline_runner.py). New_normalize_color()helper handles CSS named colors (white/black/red/etc.),rgb(r, g, b)/rgba(...)(clamped to [0, 255]), and already-hex#RRGGBB/#RGB(case preserved). Anything unrecognised falls back to#000000— a black stitch is better than a 500 that takes the whole export down.Tests added in
tests/unit/test_pipeline_runner.py:_normalize_colorfor named/hex/rgb/rgba/unknown inputs (24 cases)_group_stitches_into_objectsnamed-color regression — the actual repro:white/blackflows now produce#FFFFFF/#000000instead of crashing pydantic.121/121 tests pass, ruff clean.
Note: kilo exited 0 on the quickfix dispatch without committing source changes — same pattern as #53. Per the fixer-pipeline skill, fell back to manual fix. Also worth flagging separately: the quickfix gate currently uses the system
python(hermes-agent venv, FastAPI 0.115.6) instead of the repo venv (0.115.14), which would have made every Python-repo gate fail until that is fixed — but that is a separate gate-script issue, not part of this bug.