{"record":{"id":"96150567cfdc2a0e","repo":"sipeed/picoclaw","slug":"matrix-message-id-is-empty","errorCode":null,"errorMessage":"matrix message ID is empty","messagePattern":"matrix message ID is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/matrix/matrix.go","lineNumber":649,"sourceCode":"\tresp, err := c.client.SendMessageEvent(ctx, roomID, event.EventMessage, &event.MessageEventContent{\n\t\tMsgType: event.MsgNotice,\n\t\tBody:    text,\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn resp.EventID.String(), nil\n}\n\n// EditMessage implements channels.MessageEditor.\nfunc (c *MatrixChannel) EditMessage(ctx context.Context, chatID string, messageID string, content string) error {\n\troomID := id.RoomID(strings.TrimSpace(chatID))\n\tif roomID == \"\" {\n\t\treturn fmt.Errorf(\"matrix room ID is empty\")\n\t}\n\tif strings.TrimSpace(messageID) == \"\" {\n\t\treturn fmt.Errorf(\"matrix message ID is empty\")\n\t}\n\n\teditContent := c.messageContent(content)\n\teditContent.SetEdit(id.EventID(messageID))\n\n\t_, err := c.client.SendMessageEvent(ctx, roomID, event.EventMessage, editContent)\n\treturn err\n}\n\n// DeleteMessage implements channels.MessageDeleter.\nfunc (c *MatrixChannel) DeleteMessage(ctx context.Context, chatID string, messageID string) error {\n\troomID := id.RoomID(strings.TrimSpace(chatID))\n\tif roomID == \"\" {\n\t\treturn fmt.Errorf(\"matrix room ID is empty\")\n\t}\n\teventID := id.EventID(strings.TrimSpace(messageID))\n\tif eventID == \"\" {\n\t\treturn fmt.Errorf(\"matrix message ID is empty\")","sourceCodeStart":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/matrix/matrix.go#L631-L667","documentation":"Thrown by MatrixChannel.EditMessage when messageID is empty after trimming (pkg/channels/matrix/matrix.go:649). The message ID must be a Matrix event ID (e.g. $abc:example.org) because it is passed to editContent.SetEdit to build the replacement event. Like the room-ID check, this is a plain error with no retry sentinel, so the manager will not retry it.","triggerScenarios":"Editing a message whose event ID was never recorded (message sent by another device/user, message predating persistence, or an external system's ID mistaken for a Matrix event ID); passing a provider-agnostic message ID from a multi-channel abstraction without per-channel ID translation.","commonSituations":"Multi-channel bots storing one canonical message ID instead of per-channel IDs; edits of messages the bot did not itself send through this channel; DB rows with empty event_id after partial writes.","solutions":["Store the Matrix event ID returned by Send/SendMedia (the []string result) keyed to your internal message ID, and translate before editing","Reject edits targeting messages without a recorded Matrix event ID","Do not feed external/foreign IDs into EditMessage — only event IDs this channel emitted"],"exampleFix":"// before\n ch.EditMessage(ctx, roomID, internalMsgID, newText) // internal ID is not an event ID\n\n// after\n evID, ok := store.MatrixEventID(internalMsgID)\n if !ok { return fmt.Errorf(\"message %s has no matrix event ID; not editable\", internalMsgID) }\n ch.EditMessage(ctx, roomID, evID, newText)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(messageID) == \"\" {\n\treturn errors.New(\"edit rejected: message has no stored matrix event ID\")\n}\nif err := matrixCh.EditMessage(ctx, chatID, messageID, newText); err != nil { /* handle */ }","typeGuard":"func validMatrixEventID(messageID string) bool {\n\tid := strings.TrimSpace(messageID)\n\treturn strings.HasPrefix(id, \"$\") && strings.Contains(id, \":\")\n}","tryCatchPattern":"if err := matrixCh.EditMessage(ctx, chatID, messageID, newText); err != nil {\n\tif strings.Contains(err.Error(), \"message ID is empty\") {\n\t\tdeadLetterEdit(messageID) // permanent data gap, not retryable\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Store the event ID returned by Send/SendMedia keyed by your internal message ID","Reject edits for messages you did not send through this channel","Only pass Matrix event IDs ($...) — never external system IDs"],"tags":["go","matrix","validation","edit","event-id","message-management"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}