Skip to content

fix(sync): prevent whitespace-only board names from blocking sync - #2282

Open
RodriSanchez1 wants to merge 1 commit into
masterfrom
fix/blank-board-name-sync
Open

RodriSanchez1 wants to merge 1 commit into
masterfrom
fix/blank-board-name-sync

Conversation

@RodriSanchez1

Copy link
Copy Markdown
Collaborator

Fixes the sync-blocking half of #2281.

Problem

A board whose name is whitespace-only (" ") is accepted everywhere on the way out and rejected at the last step:

  1. TileEditor.component.js:532disableSubmit={!currentLabel} correctly blocks '', but ' ' is truthy, so Save is enabled.
  2. handleLabelChange clears labelKey, so the folder board is created with name: ' ', nameKey: ''.
  3. extractBoardName returns board.name unchanged, because ' ' is truthy.
  4. Swagger (swagger.yaml:1522-1532) declares name as required / type: string with no minLengthrequired means the key is present, not non-empty — so it passes.
  5. Mongoose (Board.js:8-12) has trim: true, and the trim setter runs before the required validator. ' ' becomes '' and the save fails.

Result: 409 from createBoard, 500 from updateBoard, and because UPDATE_BOARD sets PENDING before the call and the catch only logs, the board is re-pushed every sync cycle forever.

author fails identically — transformBoardForUser used author: userName || userEmail, and a display name of ' ' is truthy, survives the ||, and dies at the same trim + required pair.

Changes

  • TileEditor.component.jsdisableSubmit={!currentLabel || !currentLabel.trim()}. Covers both the create (Board.container.js:588) and edit (:1172) paths, since both read tile.label behind this button. Deliberately not trimming in handleLabelChange, which would fight the user mid-typing.
  • Board.utils.jsextractBoardName treats whitespace-only name and nameKey segments as missing and falls back to DEFAULT_BOARD_NAME; transformBoardForUser trims userName before the ||.
  • Board.constants.jsDEFAULT_BOARD_NAME = 'Untitled Board', the same string used before 78866f6. A plain constant rather than an i18n message because extractBoardName has no intl in scope; localizing it is a signature change worth doing separately.
  • Board.actions.jspushBoard exceptions now carry status and apiError. The API already returns the exact Mongoose message (board.js:38, board.js:262) naming the failing field, but the raw axios rejection's .message is only "Request failed with status code 500", so that detail was being discarded at the client boundary.

Scope — what this does not do

Existing affected boards are not repaired. The extractBoardName fallback only runs via transformBoardForUser, which applies to default/no-email boards. A user-owned board already in localStorage with a whitespace name goes straight to updateApiBoard and will still fail on every cycle. The fix that would cover it — trimming at the wire in API.createBoard / API.updateBoard — was intentionally deferred, and is tracked as an open item on #2281.

The retry loop is untouched. Any permanently-rejected board is still re-pushed indefinitely. That needs an attempt counter in syncMeta and interacts with the graduation logic in classifyBoardsForPush, so it warrants its own change.

One board remains unexplained. The originally-reported board reads people in the DB, which derives from nameKey: cboard.symbol.people and is non-blank under every path examined. No code path was found that blanks it. The telemetry change should settle whether it is failing on name or author on the next sync cycle from an affected user — so please don't read a drop in pushBoard exceptions as proof the diagnosis was complete.

Also worth noting: commit 78866f6 (return '' in extractBoardName) is not the regression. That branch needs a board with neither name nor nameKey; all 44 default boards in boards.json carry a nameKey deriving a non-blank name, and the tile path always supplies a truthy name. It was a latent hazard, now closed by the fallback, but the live bug is whitespace.

Testing

eslint clean on all four files. No existing test asserts on disableSubmit, on trackSyncException's pushBoard properties, or on extractBoardName / transformBoardForUser — those two helpers have no direct coverage today, which is a gap worth filling.

🤖 Generated with Claude Code

A board name of "   " passes the TileEditor submit guard and Swagger
validation, but Mongoose applies its trim setter before the required
validator, so the value becomes "" and the save is rejected. The board
stays PENDING and is re-pushed on every sync cycle indefinitely.

- Guard disableSubmit on the trimmed label so whitespace-only labels
  cannot create a folder board.
- Restore a DEFAULT_BOARD_NAME fallback in extractBoardName and treat
  whitespace-only name/nameKey values as missing.
- Trim userName before the author fallback, which fails identically.
- Record the API's response status and error detail on pushBoard
  exceptions; the axios message alone does not name the failing field.

Refs #2281

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant