usememos/memos · error

parent memo not found

Error message

parent memo not found

What it means

During a memo update, if the stored memo references a ParentUID, the helper re-fetches the parent by UID to inherit its visibility. A nil parent (UID set but no matching row) aborts with this error instead of silently keeping an orphaned reply thread. It is a data-integrity check on the parent reference.

Source

Thrown at server/router/api/v1/memo_update_helpers.go:51

		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")
	}
	memoMessage, err := s.convertMemoFromStore(ctx, memo, reactions, attachments, relations)
	if err != nil {
		return nil, nil, nil, errors.Wrap(err, "failed to convert memo")
	}

	var parentMemo *store.Memo
	if memo.ParentUID != nil {
		parentMemo, err = s.Store.GetMemo(ctx, &store.FindMemo{UID: memo.ParentUID})
		if err != nil {
			return nil, nil, nil, errors.Wrap(err, "failed to get parent memo")
		}
		if parentMemo == nil {
			return nil, nil, nil, errors.New("parent memo not found")
		}
		memoMessage.Visibility = convertVisibilityFromStore(parentMemo.Visibility)
	}

	return memo, parentMemo, memoMessage, nil
}

func (s *APIV1Service) dispatchMemoUpdatedSideEffects(ctx context.Context, memo *store.Memo, parentMemo *store.Memo, memoMessage *v1pb.Memo) {
	if err := s.DispatchMemoUpdatedWebhook(ctx, memoMessage); err != nil {
		slog.Warn("Failed to dispatch memo updated webhook", slog.Any("err", err))
	}

	visibility := memo.Visibility
	if parentMemo != nil {
		visibility = parentMemo.Visibility
	}
	s.SSEHub.Broadcast(&SSEEvent{
		Type:       SSEEventMemoUpdated,

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Re-fetch the parent (GetMemo with the ParentUID) and if missing, clear the child's parent field (set parent to empty/unset) before updating.
  2. When implementing memo deletion, detach children (clear their parent) in the same flow.
  3. Repair dangling references directly in the database or via a script if the corruption already exists.

Example fix

// before
await memoClient.updateMemo({ memo: { name, content, ...staleMemo } }); // stale parent set

// after
let parent;
try { parent = await memoClient.getMemo({ name: parentName }); } catch { parent = null; }
const memo = parent ? { ...staleMemo } : { ...staleMemo, parent: undefined };
Defensive patterns

Strategy: validation

Validate before calling

if (memo.parent) {
  const parent = await memoClient.getMemo({ name: memo.parent }).catch(() => null);
  if (!parent) memo = { ...memo, parent: undefined }; // detach orphaned reference before update
}

Try / catch

try { await memoClient.updateMemo({ memo }); }
catch (e) {
  if (e.message.includes('parent memo not found')) {
    await memoClient.updateMemo({ memo: { ...memo, parent: undefined } });
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Updating a memo whose parent_id/ParentUID points to a deleted memo; parent deleted in another session while the child edit is in flight; database restored or partially migrated leaving dangling parent references.

Common situations: Deleting a parent memo without cascade-detaching children; multi-tab editing races; imports that preserved parent UIDs from a different instance.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/1fd69f1fe4a1e33a. Report an issue: GitHub.