multica-ai/multica · warning · ErrRegistrationSessionNotFound

lark registration: session not found

Error message

lark registration: session not found

What it means

Lark registration sentinel error: the registration session id is unknown or was garbage-collected. The in-memory RegistrationService drops sessions once gcAfter passes; lookups of a missing/expired session return this error, which the handler maps to 404.

Source

Thrown at server/internal/integrations/lark/registration_service.go:698

// gcAfter when it terminates, and an expired-by-deadline session
// closes itself.
func (s *RegistrationService) gcExpiredLocked() {
	now := s.cfg.Now()
	s.mu.Lock()
	defer s.mu.Unlock()
	for id, sess := range s.sessions {
		sess.mu.Lock()
		drop := !sess.gcAfter.IsZero() && sess.gcAfter.Before(now)
		sess.mu.Unlock()
		if drop {
			delete(s.sessions, id)
		}
	}
}

// ErrRegistrationSessionNotFound is what the service returns for
// unknown / GC'd sessions. The handler maps it to 404.
var ErrRegistrationSessionNotFound = errors.New("lark registration: session not found")

func randomSessionID() (string, error) {
	buf := make([]byte, 24)
	if _, err := rand.Read(buf); err != nil {
		return "", err
	}
	return base64.RawURLEncoding.EncodeToString(buf), nil
}

func uuidEqual(a, b pgtype.UUID) bool {
	if !a.Valid || !b.Valid {
		return false
	}
	return a.Bytes == b.Bytes
}

// botNamePreset builds the display name we pre-fill on Lark's
// PersonalAgent creation form so the installed bot reads

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Start the registration flow over to mint a new session id.
  2. If running multiple replicas, pin the registration flow to one instance (sticky routing) or back sessions with shared storage.
  3. Treat 404-on-resume as a normal flow outcome: the UI should offer 'start again' rather than an error page.

Example fix

// before
sess, err := svc.Resume(ctx, sessionID)
if err != nil {
	return err // 500-ish leak
}

// after
sess, err := svc.Resume(ctx, sessionID)
if errors.Is(err, lark.ErrRegistrationSessionNotFound) {
	http.NotFound(w, r) // client restarts the flow
	return
}
Defensive patterns

Strategy: try-catch

Try / catch

sess, err := regSvc.Resume(ctx, sessionID)
if err != nil {
	if errors.Is(err, lark.ErrRegistrationSessionNotFound) {
		// expired/GC'd/restart-wiped: restart the flow, do not retry the id
		return redirectToStart(w)
	}
	return err
}

Prevention

When it happens

Trigger: Polling or resuming a lark registration flow with a session id that never existed, expired (GC sweep deleted it), or belonged to a previous server process — the session map is per-process memory, not durable storage.

Common situations: Service restart or redeploy mid-registration wipes the in-memory map while the user's browser holds an old session id; user walks away longer than the session lifetime then clicks continue; load-balanced replicas where the follow-up lands on a different instance than the one that created the session.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/3a9e6e3ec4b84773. Report an issue: GitHub.