client.fetchMessages() reads them back from the server, always ordered oldest to newest. This guide covers the cursors, the paging behavior, and what you get back.
fetchMessages reads server-channel history only. There is no DM history read in this SDK. A bot sees direct messages as they arrive on messageCreate and cannot page back through them. See Direct messages.Read the newest messages
With no cursor,fetchMessages(serverId, channelId, opts?) returns the newest messages in a channel, oldest to newest. limit defaults to 50.
limit fetches across as many pages as it takes.
The wire page sizes are fixed: the first page holds up to 50 rows, and each page after it up to 25. A
limit of 100 therefore costs several round-trips. The read stops early when a short page proves the history is exhausted in that direction.Cursors are a Date or a Message
You point a read at a moment in time with a cursor. A cursor is always aDate or a Message, never a message id.
Cloak message ids are random UUIDv4 values, so they do not sort by time. There is no id cursor. When you pass a
Message as a cursor, the read uses that message’s createdAt. A Message with no createdAt throws.before, after, and around.
Page backward with before
before returns messages strictly older than the cursor. The newest limit of them are kept, and the result is still ordered oldest to newest.
Message to walk backward from one you already have. This is the usual way to load older history a page at a time.
before is auto-paginated.
Page forward with after
after returns messages strictly newer than the cursor. The limit closest to the cursor are returned, so the oldest matching messages are the ones you keep.
after is auto-paginated.
Fetch context around a message
around returns a single window of about 50 messages straddling the cursor. Reach for it when you want the context on either side of one message.
around is the one read that is not auto-paginated. It returns a single window and ignores limit for paging.Options
Date | Message
Return messages strictly older than the cursor. Auto-paginated.
Date | Message
Return messages strictly newer than the cursor. Auto-paginated.
Date | Message
Return a single window of about 50 messages straddling the cursor. Not auto-paginated.
number
default:"50"
How many messages to keep. Applies to
before, after, and a cursorless read. around ignores it for paging.string
The channel’s group, where you already know it.
The whole paged read runs as one serialized action, so the channel selection cannot move mid-read. You get a consistent slice even when several pages are fetched under the hood.
Returned messages are live
Every message fromfetchMessages is fully actionable, exactly like one you received on the firehose. Each carries reply, channel.send, react, unreact, edit, delete, pin, and unpin, plus a pinned boolean.
The same fields are populated too. mentions, mentionsMe, and repliedTo are computed for history exactly as they are for a live message, because both paths run through the same builder.
serverId, because this read is server-channel only. That is why the loop above can call msg.pin() without a guard. A live messageCreate message is different: serverId is null for a DM, and the mutation methods reject there. Guard with if (msg.isDM || !msg.serverId) return; before you carry this pattern into a messageCreate handler.
See the Message type for the full shape.
Undecryptable messages
Each message decrypts with the key for its own epoch. If a row cannot be decrypted, itscontent is the empty string ''. You never receive raw ciphertext, so a simple check is enough to skip a message you cannot read.
''.
Requires the message_read_history permission
fetchMessages requires the message_read_history permission. If the server has not granted it, the promise rejects with a CloakActionError whose .permission is 'message_read_history'.
Find where a message lives
client.messageLocation(messageId) tells you where a message the bot has seen lives, whether it arrived live or through fetchMessages. It returns a MessageLocation, or undefined when the message is not in the cache.
string | null
The server the message lives in, or
null when the remembered message is a direct message. In that case channelId holds the dm id and is the whole location.string
The channel within that server, or the dm id when
serverId is null.string
The channel’s group, where known.
string
The id of the message’s author.
number | null
When the message was created, in milliseconds, or
null if unknown.The lookup is a bounded, best-effort cache of about the last 5000 messages the bot has seen. Older messages fall out, and
messageLocation returns undefined for them.Next
The Message object
Everything a returned message can do.
Direct messages
Why DM history is out of scope, and what you get instead.
Mentions and replies
How mentions and mentionsMe are derived.
Permissions
What message_read_history grants and how.