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

  1. Re-fetch the memo (GetMemo) before editing; if it 404s, refresh the list and drop the stale row.
  2. Handle the NotFound-equivalent error in the UI by showing 'memo no longer exists' and reloading.
  3. 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

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.