usememos/memos · error

invalid uid

Error message

invalid uid

What it means

Returned by Store.CreateMemo when the supplied memo UID does not match base.UIDMatcher: ^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,34}[a-zA-Z0-9])?$ — 1 to 36 characters, must start and end with a letter or digit, and may contain only letters, digits and dashes in between. UIDs become resource names (memos/<uid>), so the format is enforced before the row reaches any database driver. This is a normal API-level validation error, not a startup failure.

Source

Thrown at store/memo.go:111

type UpdateMemo struct {
	ID         int32
	UID        *string
	CreatedTs  *int64
	UpdatedTs  *int64
	RowStatus  *RowStatus
	Content    *string
	Visibility *Visibility
	Pinned     *bool
	Payload    *storepb.MemoPayload
}

type DeleteMemo struct {
	ID int32
}

func (s *Store) CreateMemo(ctx context.Context, create *Memo) (*Memo, error) {
	if !base.UIDMatcher.MatchString(create.UID) {
		return nil, errors.New("invalid uid")
	}
	return s.driver.CreateMemo(ctx, create)
}

func (s *Store) ListMemos(ctx context.Context, find *FindMemo) ([]*Memo, error) {
	return s.driver.ListMemos(ctx, find)
}

func (s *Store) GetMemo(ctx context.Context, find *FindMemo) (*Memo, error) {
	list, err := s.ListMemos(ctx, find)
	if err != nil {
		return nil, err
	}
	if len(list) == 0 {
		return nil, nil
	}

	memo := list[0]

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Send a uid of 1-36 characters using only [a-zA-Z0-9-], starting and ending with a letter or digit (replace '_' with '-').
  2. If you do not need a custom uid, omit the field and let the server generate one.
  3. If parsing a resource name like "memos/abc123", pass only the segment after the slash.

Example fix

// before
memo, err := store.CreateMemo(ctx, &store.Memo{ UID: "my_note_2024!", Content: &content })

// after
memo, err := store.CreateMemo(ctx, &store.Memo{ UID: "my-note-2024", Content: &content })
Defensive patterns

Strategy: validation

Validate before calling

// Go callers: validate before CreateMemo
var uidMatcher = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,34}[a-zA-Z0-9])?$`)
if create.UID != "" && !uidMatcher.MatchString(create.UID) {
    return errors.New("uid must be 1-36 chars of [a-zA-Z0-9-], alphanumeric at both ends")
}
memo, err := s.CreateMemo(ctx, create)

Type guard

func isValidMemoUID(uid string) bool {
    ok := len(uid) >= 1 && len(uid) <= 36
    if !ok {
        return false
    }
    for i, r := range uid {
        alnum := (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')
        if i == 0 || i == len(uid)-1 {
            if !alnum {
                return false
            }
        } else if !alnum && r != '-' {
            return false
        }
    }
    return true
}

Try / catch

// In service layer: map store validation errors to InvalidArgument
memo, err := s.Store.CreateMemo(ctx, create)
if err != nil {
    if strings.Contains(err.Error(), "invalid uid") {
        return nil, status.Errorf(codes.InvalidArgument, "uid must match [a-zA-Z0-9-]{1,36} with alphanumeric ends")
    }
    return nil, status.Errorf(codes.Internal, "failed to create memo")
}

Prevention

When it happens

Trigger: Calling the memo-creation API path that sets a custom uid with an empty string, a uid containing underscores/dots/slashes, a uid longer than 36 characters, or one starting/ending with a dash. Auto-generated UIDs that collide with these rules (rare) would surface the same error.

Common situations: Clients importing notes from another system and reusing foreign slugs with underscores; trimming/normalizing loops that produce an empty uid; resource-name parsing that passes 'memos/abc' as the uid instead of 'abc'.

Related errors


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