Files
bambuddy/backend
maziggy d44873b36e fix(inventory): one structured 409 for a tag another spool holds (issue #3110)
The two tag-link routes answered the same conflict differently. The
    built-in one said "Tag UID already linked to another active spool" and
    named nobody -- while holding the conflicting spool row it had just
    loaded -- and Spoolman mode named the spool inside a different English
    sentence. Neither was machine-readable, so a client had to parse prose
    to learn which spool to look at, and could only do it in one mode.

    Both now raise one shared constructor: code tag_already_linked, the
    holder's id, and which identifier collided. That is the detail shape
    insufficient_filament and printer_connection_failed already use, so
    ApiError parses it with no frontend change.

    Two active spools can carry one tag -- no unique index on either
    column, no conflict check on PATCH /spools/{id}, and /spools/bulk
    copies one payload including the tag into every row it creates -- and
    the lookup read that with scalar_one_or_none(), which raises on two
    rows. The exception escaped into the auth middleware's fail-closed
    handler, so the caller was told the authentication service was
    unavailable. Both lookups are now ordered and take the first row, as
    get_spool_by_tag earlier in the same file always has.

    Naming the lowest id means the Spoolman scan reads every row where it
    used to stop at its first match, so it now reads extra.tag defensively:
    that field is edited outside Bambuddy, and a single null further down
    the list would otherwise take the request down in place of the 409.

    The kiosk reads the new code: a refused link showed a flat "Failed to
    assign spool" and now names the spool holding the tag, reusing the
    inventory.tagAlreadyLinked key that no code referenced.
2026-09-20 13:41:08 +02:00
..