This release includes breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Topics
+2 more
Affected surfaces
Summary
AI summaryUpdates https://github.com/wekan/wekan/issues/3252, req.body.members, and https://github.com/wekan/wekan/issues/3697 across a mixed release.
Full changelog
v9.69 2026-06-23 WeKan ® release
This release fixes the following bugs:
- Can't edit a card's members in the UI after removing them via the REST API,
#3697: clearing a card'smembers(orassignees) over REST left the
field uneditable in the UI. The card PUT handler guarded withif (req.body.members)— falsy for the two natural
clear payloads (nulland"") — so "remove the last member" was a silent no-op, and its string branch was written
to storenullrather than[]; a card withmembers: nullthen breaks UI code that treats it as an array. The
handler now uses an!== undefinedguard plus a shared coercion helper so any clear payload (null/""/[])
stores a cleanString[](never null), a single id is wrapped into an array, and stray non-string entries are
dropped;getMembers()/getAssignees()also coerce a legacynullto[]on read so existing corrupted documents
edit cleanly. Pure logic inmodels/lib/restArrayParam.jswith a Meteor-free Node unit test
(tests/restArrayParam.test.cjs,npm run test:unit:node, incl. a negative test reproducing the old
null-writing logic), plus a REST regression intests/playwright/specs/17-rest-api.e2e.js. - Can't create a card with no member via the REST API,
#2875: the card-create endpoints (POST .../cardsand
POST .../cards/bulk) wrotereq.body.members/assigneesstraight to the insert with no normalization — the
create-side twin of #3697 — so anull/""payload persisted asnull(breaking later UI editing). Both handlers
now run the samecoerceRestArrayParamhelper: the field is omitted when not provided (so the schema default[]
applies), any clear payload becomes[](never null), and a single id is wrapped into an array. REST regression in
tests/playwright/specs/17-rest-api.e2e.js. - Board created through the REST API shows in the API but not in the browser UI,
#5650:POST /api/boardsset the board's sole member'suserIdfrom
req.body.ownerwith no fallback, so a request that omitsownercreated a board whose only member had
userId: undefined. The board-list publication matchesmembers.$elemMatch: { userId, isActive: true }, which can
never match an undefined id — so the board was returned by the REST API but invisible in the browser.ownernow
falls back to the authenticated caller (req.body.owner || req.userId), mirroring the Meteor create method. REST +
DB regression intests/playwright/specs/17-rest-api.e2e.js. - Copying a card to another board left its comments on the wrong board,
#5166:CardComments.copy()cloned a comment but only changed its
cardId, so a card copied to another board produced comments that kept the source board'sboardId. Comment
permission checks key off the comment'sboardId, so edit/delete on a copied comment was validated against the wrong
board, and any board-scoped query saw it on the old board.copy()now also sets the destinationboardId(the
authoruserIdis intentionally preserved). Cross-board copy regression in
tests/playwright/specs/17-rest-api.e2e.js. Note: the related "wrong author shown" symptom is a separate display
issue — when a copied comment's author is not a member of the destination board, their user document isn't
published there, so the UI can't resolve the name; adding those users to the board resolves it. A broader fix
(publishing comment authors' minimal profile on boards where their comments appear) is left as a follow-up. - Exception "Removed nonexistent document" when deleting a card detail,
#3252 (partial): deleting a comment or checklist/checklist-item could
throwRemoved nonexistent documenton the client. The delete handlers calledCollection.remove(_id)directly, but
under heavy archive/delete churn the target doc can already have been evicted from the client's Minimongo cache, and
removing a missing_idthrows. The comment / checklist / checklist-item delete handlers now check the doc still
exists in the local cache before removing it
(client/components/activities/comments.js,client/components/cards/checklists.js). The server-side
before.removehooks were already hardened to guard + log instead of throwing. (No automated regression for the
thrown exception — the eviction race is not deterministically reproducible; the guard itself is a self-evident
findOne-then-remove.) The high CPU on bulk delete the issue also reports is reduced by the cascade change below;
the remaining cost is on the archive path (a bulk update, not a delete) and is a separate follow-up. - Performance: deleting a card no longer fans out per-child activity churn (#3252,
#5322).cardRemoverremoved a card's checklist items, checklists and
comments with the hookedremoveAsync, so collection-hooks fired each child'sbefore.removeonce per document —
every one doing agetCardand inserting an activity (e.g. aremovedChecklistItemper item), which then triggered
the notification + publication observers. Deleting (or bulk-deleting) a card with many children produced an
activity-insert storm and pegged the CPU.cardRemovernow removes those children with.direct(skipping the
per-child hooks — they only logged activities that are unviewable once the card is gone) and clears all of the
card's activities in a single bulk op, which also removes the activities that a delete previously left orphaned. The
deleteCardactivity is still logged afterward (so the outgoing webhook fires), and attachments still go through the
normal remove so their files are deleted. Cascade + activity-cleanup regression (also a negative test) in
tests/playwright/specs/17-rest-api.e2e.js.
and this issue is verified resolved in current code (could not reproduce / no error observed here; re-test on the
reporter's data requested):
- #2292 (archiving a swimlane appeared to delete all its cards):
Swimlane.archive()(models/swimlanes.js) only setsarchived: trueon the swimlane — it
does not touch or delete the cards, andrestore()brings the swimlane and its cards back. While a swimlane is
archived its cards are merely hidden from the board view (board queries filter toarchived: falseswimlanes), not
lost — the v2.27-era cascade described in the report no longer happens. (A dedicated way to view an archived
swimlane's cards before restoring would be a separate UX improvement.)
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About wekan
The Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . New English strings for new features at imports/i18n/data/en.i18n.json . Non-English translations at https://app.transifex.com/wekan/wekan only.
Related context
Related tools
Beta — feedback welcome: [email protected]