Skip to content

fix(provider-microsoft): return structured failure when reply createReplyAll fails (#50)#54

Merged
stevenobiajulu merged 2 commits into
mainfrom
fix/graph-reply-fallback
Apr 28, 2026
Merged

fix(provider-microsoft): return structured failure when reply createReplyAll fails (#50)#54
stevenobiajulu merged 2 commits into
mainfrom
fix/graph-reply-fallback

Conversation

@stevenobiajulu
Copy link
Copy Markdown
Member

Summary

  • Drop the silent sendMail fallback in GraphEmailProvider.replyToMessage. When createReplyAll (or any subsequent step in prepareReplyDraft / /send) throws, return a structured { success: false, error: { code: 'REPLY_FAILED', recoverable: false } } instead of a fresh email to opts?.cc.
  • Mirrors the failure shape already used by createReplyDraft.
  • Replace the existing test that asserted the broken behavior; add 4 new failure-path tests (createReplyAll throw, opts.cc regression guard, /send failure, PATCH failure inside the helper).

Why not "hydrate then sendMail"?

A sendMail-based reply has no In-Reply-To / References headers, so even with the original to / subject / cc correctly hydrated, recipients' clients won't thread it. Faking a reply via sendMail is structurally inferior to a loud failure — the caller is in a better position to decide whether to send a fresh email.

Scope note

This is a provider-layer fix. replyToEmailAction already prefetches the original message via ctx.provider.getMessage(input.message_id) (packages/email-core/src/actions/reply.ts:102), so the issue's literal repro (delete the message, then call reply_to_email) currently surfaces as PROVIDER_UNAVAILABLE from the lazy server wrapper, not from replyToMessage. The provider bug is real and worth fixing for any other failure mode (auth, transient errors, malformed IDs that pass getMessage but fail createReplyAll); aligning the action layer to translate MESSAGE_NOT_FOUND into REPLY_FAILED is a separate concern.

Test plan

  • npm run -w @usejunior/provider-microsoft test:run — 114/114 (4 new tests added).
  • npm run test:run — all suites green (525 tests across 4 packages).
  • npm run build — clean.
  • npm run lint — clean.
  • Manual: skipped. Reproducing requires a real Graph 4xx/5xx, which isn't safe to run against the user's mailbox. Mocked tests (especially the "no sendMail call after failure" assertion) are the regression guard.

Closes #50

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

stevenobiajulu added a commit that referenced this pull request Apr 28, 2026
Update the Draft-Then-Send via createReplyAll requirement to reflect that
replyToMessage now returns a structured REPLY_FAILED instead of silently
falling back to sendMail. Replace the stale "Fallback to sendMail on 404"
scenario with one that captures the new contract.

Spotted by Gemini and Codex during peer review of #54.
@stevenobiajulu stevenobiajulu enabled auto-merge (squash) April 28, 2026 14:18
…eplyAll fails

When createReplyAll threw inside replyToMessage (e.g., the original message
was deleted and Graph returned 404), the catch fell through to a manual
sendMessage that used opts?.cc as `to:` and a literal `'Re: '` as subject.
The result was success: true with the email going to the wrong recipients
(or no one) and no thread context — silent mis-send.

Replace the silent fallback with a structured failure that mirrors
createReplyDraft:

  { success: false, error: { code: 'REPLY_FAILED', message, recoverable: false } }

A sendMail-based "fallback reply" is not actually a reply: it lacks
In-Reply-To / References headers so recipients' clients won't thread it,
and no amount of hydrating to/subject fixes that. Loud failure is correct;
the caller decides whether to send a fresh email.

Tests:
- Replace the broken-fallback scenario that asserted success: true.
- Add four failure-path tests covering createReplyAll throw, opts.cc
  regression guard, /send failure after a successful PATCH, and PATCH
  failure inside prepareReplyDraft.

Closes #50
stevenobiajulu added a commit that referenced this pull request Apr 28, 2026
Update the Draft-Then-Send via createReplyAll requirement to reflect that
replyToMessage now returns a structured REPLY_FAILED instead of silently
falling back to sendMail. Replace the stale "Fallback to sendMail on 404"
scenario with one that captures the new contract.

Spotted by Gemini and Codex during peer review of #54.
@stevenobiajulu stevenobiajulu force-pushed the fix/graph-reply-fallback branch from 4188031 to d901989 Compare April 28, 2026 14:21
Update the Draft-Then-Send via createReplyAll requirement to reflect that
replyToMessage now returns a structured REPLY_FAILED instead of silently
falling back to sendMail. Replace the stale "Fallback to sendMail on 404"
scenario with one that captures the new contract.

Spotted by Gemini and Codex during peer review of #54.
@stevenobiajulu stevenobiajulu force-pushed the fix/graph-reply-fallback branch from d901989 to 444e9cc Compare April 28, 2026 18:22
@stevenobiajulu stevenobiajulu merged commit c5fdd08 into main Apr 28, 2026
13 checks passed
@stevenobiajulu stevenobiajulu deleted the fix/graph-reply-fallback branch April 28, 2026 18:25
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.

Microsoft replyToMessage fallback drops original sender and subject

1 participant