sipeed/picoclaw · warning
matrix room ID is empty
Error message
matrix room ID is empty
What it means
Thrown by MatrixChannel.StartTyping when chatID is empty after trimming (pkg/channels/matrix/matrix.go:587). It returns a no-op stop function alongside the error, so callers that ignore the error still get a safe closure. Note the distinct behavior one branch above: when the channel is not running, StartTyping returns a no-op with NO error — only a blank room ID errors. Typing is cosmetic, so failures here should never break message flow.
Source
Thrown at pkg/channels/matrix/matrix.go:587
}
}
if hasTrackedMsg {
c.dismissTrackedToolFeedbackMessage(ctx, msg.ChatID, trackedMsgID)
}
return eventIDs, nil
}
// StartTyping implements channels.TypingCapable.
func (c *MatrixChannel) StartTyping(ctx context.Context, chatID string) (func(), error) {
if !c.IsRunning() {
return func() {}, nil
}
roomID := id.RoomID(strings.TrimSpace(chatID))
if roomID == "" {
return func() {}, fmt.Errorf("matrix room ID is empty")
}
session := newTypingSession()
c.typingMu.Lock()
if prev := c.typingSessions[chatID]; prev != nil {
prev.stop()
}
c.typingSessions[chatID] = session
c.typingMu.Unlock()
parent := c.baseContext()
go c.typingLoop(parent, roomID, session)
var once sync.Once
stop := func() {
once.Do(func() {
session.stop()View on GitHub (pinned to 49183d7e8d)
Solutions
- Skip the typing call when chatID is blank — degrade silently rather than surfacing an error
- Fix the caller to pass the same trimmed room ID used for Send
- Treat any error from StartTyping as non-fatal: log at debug level and continue
Example fix
// before
stop, err := ch.StartTyping(ctx, chatID)
if err != nil { return err } // typing failure aborts the flow
// after
stop, err := ch.StartTyping(ctx, chatID)
if err != nil { logger.DebugC("matrix", "typing indicator skipped: " + err.Error()) }
defer stop() // no-op closure is safe either way Defensive patterns
Strategy: validation
Validate before calling
// typing is cosmetic: skip silently when unsendable
var stop func() = func() {}
if id := strings.TrimSpace(chatID); id != "" {
s, err := matrixCh.StartTyping(ctx, id)
if err == nil { stop = s }
} Type guard
func canShowTyping(chatID string) bool {
return strings.TrimSpace(chatID) != ""
} Try / catch
stop, err := matrixCh.StartTyping(ctx, chatID)
if err != nil {
log.Debug("typing indicator unavailable: ", err) // never propagate: UI degrades gracefully
}
defer stop() // safe: returns a no-op closure on every error path Prevention
- Never let typing errors bubble into user flows — they are cosmetic only
- Always defer the returned stop function; it is a safe no-op on error
- Pass the exact trimmed room ID you use for Send
When it happens
Trigger: Calling StartTyping with an empty/whitespace chat ID from a UI/handler that lost its conversation binding; calling it before the chat ID is established (e.g. during onboarding when no inbound Matrix event exists yet).
Common situations: Frontend handlers invoking typing on selection of a not-yet-opened conversation; relays synthesizing typing notifications without a mapped room; races where typing is requested while a conversation is being created.
Related errors
- matrix room ID is empty: %w
- no media store available: %w
- matrix message ID is empty
- empty matrix media URL
- invalid matrix media URL: %s
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/d8a4edbc987d820f.
Report an issue: GitHub.