usememos/memos · error
memo not found
Error message
memo not found
What it means
buildUpdatedMemoState loads the memo by ID before applying an update; if the store lookup returns no row, this error aborts the update flow. Callers typically translate it into a NotFound status, but as thrown it is a bare store-level miss (GetMemo succeeded, row absent).
Source
Thrown at server/router/api/v1/memo_update_helpers.go:19
package v1
import (
"context"
"log/slog"
"github.com/pkg/errors"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
"github.com/usememos/memos/store"
)
func (s *APIV1Service) buildUpdatedMemoState(ctx context.Context, memoID int32) (*store.Memo, *store.Memo, *v1pb.Memo, error) {
memo, err := s.Store.GetMemo(ctx, &store.FindMemo{ID: &memoID})
if err != nil {
return nil, nil, nil, errors.Wrap(err, "failed to get memo")
}
if memo == nil {
return nil, nil, nil, errors.New("memo not found")
}
memoName := buildMemoName(memo.UID)
reactions, err := s.Store.ListReactions(ctx, &store.FindReaction{
ContentID: &memoName,
})
if err != nil {
return nil, nil, nil, errors.Wrap(err, "failed to list reactions")
}
attachments, err := s.Store.ListAttachments(ctx, &store.FindAttachment{
MemoID: &memo.ID,
})
if err != nil {
return nil, nil, nil, errors.Wrap(err, "failed to list attachments")
}
relations, err := s.loadMemoRelations(ctx, memo)
if err != nil {
return nil, nil, nil, errors.Wrap(err, "failed to load memo relations")View on GitHub (pinned to 14d757ce1f)
Solutions
- Re-fetch the memo (GetMemo) before editing; if it 404s, refresh the list and drop the stale row.
- Handle the NotFound-equivalent error in the UI by showing 'memo no longer exists' and reloading.
- Verify the memo name/UID in the request matches an existing resource on this instance.
Example fix
// before
await memoClient.updateMemo({ memo: { name: memoName, content: newText } });
// after
try {
await memoClient.updateMemo({ memo: { name: memoName, content: newText } });
} catch (e) {
if (e.code === 'not_found') { await queryClient.invalidateQueries(['memos']); return; }
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm the memo still exists before editing
const m = await memoClient.getMemo({ name: memoName }).catch(() => null);
if (!m) throw new Error('Memo no longer exists; refresh the list'); Try / catch
try {
await memoClient.updateMemo({ memo });
} catch (e) {
if (e.message.includes('memo not found') || e.code === 'not_found') {
queryClient.invalidateQueries({ queryKey: ['memos'] });
notify('This memo was deleted elsewhere');
return;
}
throw e;
} Prevention
- Keep memo list caches short-lived or invalidate on window focus to catch cross-tab deletions.
- Show a 'resource changed/deleted' path in edit UIs, not just a generic error.
- Re-fetch before applying updates to long-open editors.
When it happens
Trigger: UpdateMemo with a name whose parsed ID does not exist (deleted memo, wrong UID, ID from another instance); SetMemoResources/SetMemoRelations on a memo deleted by another session between read and write; retrying an update after the memo was removed.
Common situations: Stale UI after a memo was deleted in another tab or by a webhook; client caching memo lists past deletion; concurrent multi-device editing.
Related errors
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/c179498072200368.
Report an issue: GitHub.