Skip to content

Gracefully handle missing scope in UserMemoryStore (fixes #564)#566

Merged
eanzhao merged 2 commits intofeature/lark-botfrom
fix/2026-05-04_user-memory-scope
May 4, 2026
Merged

Gracefully handle missing scope in UserMemoryStore (fixes #564)#566
eanzhao merged 2 commits intofeature/lark-botfrom
fix/2026-05-04_user-memory-scope

Conversation

@eanzhao
Copy link
Copy Markdown
Contributor

@eanzhao eanzhao commented May 4, 2026

Summary

  • ActorBackedUserMemoryStore throws InvalidOperationException when no HTTP auth context is available (Orleans actor / channel conversation)
  • Adds TryResolveScopeId / TryResolveWriteActorId that return null instead of throwing
  • ReadProjectedStateAsync returns null when scope is unavailable → GetAsync returns EmptyBuildPromptSectionAsync returns empty string silently
  • Write operations still throw since they only run from Studio API with auth context
  • Eliminates the recurring warning on every LLM turn in channel conversations

Verification

  • dotnet build — 0 errors
  • dotnet test test/Aevatar.AI.Tests — 500 passed, 0 failed

Closes #564

Channel conversation LLM runs inside Orleans actors with no HTTP auth
context. The scope resolver returns null, causing
ActorBackedUserMemoryStore to throw InvalidOperationException on every
turn and log a warning.

- Add TryResolveScopeId/TryResolveWriteActorId that return null instead
  of throwing
- ReadProjectedStateAsync returns null when no scope is available
- GetAsync returns UserMemoryDocument.Empty, BuildPromptSectionAsync
  returns empty string — no warning logged
- Write operations (Save/Add/Remove) still throw since they only run
  from Studio API with auth context

Closes #564
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6765f706a9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +202 to +203
if (actorId is null)
return null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve failure signal when scope is missing for deletes

Returning null from ReadProjectedStateAsync when scope resolution fails changes RemoveEntryAsync behavior from throwing to silently returning false (because it treats state is null as "entry not found"). In authenticated write paths, this masks a scope-resolution/configuration error as a normal miss, so callers can falsely believe deletion succeeded semantically (no such entry) instead of seeing the actual routing failure and handling it.

Useful? React with 👍 / 👎.

@eanzhao
Copy link
Copy Markdown
Contributor Author

eanzhao commented May 4, 2026

Verdict

The fix correctly addresses issue #564GetAsync and BuildPromptSectionAsync no longer throw on nil scope, eliminating the recurring warning log. The approach of adding TryResolve* variants and guarding the read path is sound and minimal.

However, there are a few things that should be addressed before merging:


1. RemoveEntryAsync silently returns false — inconsistent with PR description

The PR body states:

Write operations (Save/Add/Remove) still throw since they only run from Studio API with auth context

But in practice RemoveEntryAsync does NOT throw when scope is nil — it calls ReadProjectedStateAsync (now Try*) and hits the state is null short-circuit, returning false:

// ActorBackedUserMemoryStore.cs:126-131
public async Task<bool> RemoveEntryAsync(string id, CancellationToken ct = default)
{
    var state = await ReadProjectedStateAsync(ct);
    if (state is null || !state.Entries.Any(...))
        return false;  // <— returns false without throwing
    ...
}

SaveAsync and AddEntryAsync still throw (via EnsureWriteActorAsync), so the inconsistency is real. Either:

  • Make RemoveEntryAsync consistently throw by guarding the write path separately from the read check, or
  • Update the PR description to reflect what actually happens.

In practice this is harmless since RemoveEntryAsync is only called from Studio API paths (which have scope), but it's worth fixing for clarity.

2. Missing test for nil-scope read path

The key behavioral change — GetAsync returns Empty (not throws) when scope is nil — has no test coverage. The existing UserMemoryStore_NoScope_Throws test only covers AddEntryAsync (write path). Consider adding something like:

UserMemoryStore_GetAsync_NoScope_ReturnsEmpty
UserMemoryStore_BuildPromptSectionAsync_NoScope_ReturnsEmpty

3. (Minor) BuildPromptSectionAsync try-catch is now dead for scope errors

After this fix, scope errors never reach the catch block in BuildPromptSectionAsync. The catch is still useful as defense-in-depth for projection store failures, but it's worth noting that the "Failed to load user memory for prompt injection" log message will now only fire for non-scope errors, which might be misleading when debugging.


Overall the fix is correct and does its job. The issues above are quality/clarity concerns rather than functional bugs.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.02%. Comparing base (00fa7c7) to head (f1ddc4b).
⚠️ Report is 3 commits behind head on feature/lark-bot.

@@                 Coverage Diff                  @@
##           feature/lark-bot     #566      +/-   ##
====================================================
+ Coverage             72.01%   72.02%   +0.01%     
====================================================
  Files                  1255     1255              
  Lines                 90723    90724       +1     
  Branches              11877    11880       +3     
====================================================
+ Hits                  65331    65344      +13     
+ Misses                20706    20699       -7     
+ Partials               4686     4681       -5     
Flag Coverage Δ
ci 72.02% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...tructure/ActorBacked/ActorBackedUserMemoryStore.cs 88.19% <100.00%> (+1.32%) ⬆️

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@eanzhao eanzhao merged commit 6505a06 into feature/lark-bot May 4, 2026
12 checks passed
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