Say so when the server refuses a subscribe #102

Closed
opened 2026-08-27 18:32:47 +00:00 by jcoffey-dev · 0 comments
Owner

Adding a shared address book did nothing in Firefox and worked in Chrome. The button wasn't broken — the refusal was invisible.

Subscribing is the one call in the app that writes to somebody else's account, so it's the one a perfectly healthy server is entitled to say no to. And JMAP says no to a /set by answering successfully, with the object listed in notUpdated.

Neither subscribe method looked. The promise resolved, the code carried on, the re-read came back unchanged, and the row stayed exactly where it was with nothing said.

Every other /set in this codebase reads notUpdated and raises — files.ts:313 and :323, for two. These two were written without it. That's the whole defect: not a wrong answer, an unread one.

Both now check it and surface what the server actually said.

What this does not do

Fix the underlying refusal, because we don't know what it is yet. That's the point — whatever Stalwart is objecting to can now be read off the screen instead of inferred from which browser happened to be in front of you. My guess is that the two browsers are signed in as different accounts (owner vs sharee) and a read-only sharee cannot write isSubscribed on the owner's book, but that is a guess and the toast will replace it with the server's own words.

328 web and 77 server tests pass. npm run typecheck clean.

🤖 Generated with Claude Code

Merged 2026-08-27 as coffey-labs/ihasmail@04ec57058a

Rebuilt from: git history, session transcript.

Adding a shared address book did nothing in Firefox and worked in Chrome. The button wasn't broken — **the refusal was invisible.** Subscribing is the one call in the app that writes to somebody *else's* account, so it's the one a perfectly healthy server is entitled to say no to. And JMAP says no to a `/set` by answering **successfully**, with the object listed in `notUpdated`. Neither subscribe method looked. The promise resolved, the code carried on, the re-read came back unchanged, and the row stayed exactly where it was with nothing said. Every other `/set` in this codebase reads `notUpdated` and raises — `files.ts:313` and `:323`, for two. These two were written without it. That's the whole defect: not a wrong answer, an unread one. Both now check it and surface what the server actually said. ### What this does not do Fix the underlying refusal, because we don't know what it is yet. That's the point — whatever Stalwart is objecting to can now be read off the screen instead of inferred from which browser happened to be in front of you. My guess is that the two browsers are signed in as different accounts (owner vs sharee) and a read-only sharee cannot write `isSubscribed` on the owner's book, but that is a guess and the toast will replace it with the server's own words. 328 web and 77 server tests pass. `npm run typecheck` clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) **Merged** 2026-08-27 as coffey-labs/ihasmail@04ec57058a64 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.