langgenius/dify · error · NotFound
First Message Not Exists.
Error message
First Message Not Exists.
What it means
HTTP 404 raised by GET /installed-apps/<id>/messages when MessageService raises FirstMessageNotExistsError. The first_id query parameter (the pagination anchor) does not reference a real message in the conversation.
Source
Thrown at api/controllers/console/explore/message.py:111
args.conversation_id,
args.first_id or None,
args.limit,
session=session,
)
adapter = TypeAdapter(ExploreMessageListItem)
items = [
adapter.validate_python(MessageResponseSource(message, session=session), from_attributes=True)
for message in pagination.data
]
return ExploreMessageInfiniteScrollPagination(
limit=pagination.limit,
has_more=pagination.has_more,
data=items,
).model_dump(mode="json")
except ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")
except FirstMessageNotExistsError:
raise NotFound("First Message Not Exists.")
@console_ns.route(
"/installed-apps/<uuid:installed_app_id>/messages/<uuid:message_id>/feedbacks",
endpoint="installed_app_message_feedback",
)
class MessageFeedbackApi(InstalledAppResource):
@console_ns.expect(console_ns.models[MessageFeedbackPayload.__name__])
@console_ns.response(200, "Feedback submitted successfully", console_ns.models[ResultResponse.__name__])
@with_current_user
@model_validate(MessageFeedbackPayload)
def post(
self, req_data: MessageFeedbackPayload, current_user: Account, installed_app: InstalledApp, message_id: UUID
):
app_model = installed_app.app_with_session(session=db.session())
if app_model is None:
raise AppUnavailableError()
View on GitHub (pinned to ef8544b173)
Solutions
- On 404 with this message, discard the stored first_id cursor and re-fetch the first page.
- Encode first_id exactly as returned by the previous response (UUID string, no truncation).
- If deep-linking, validate the message id is still present before scrolling to it.
- Handle message-deletion events from the realtime channel to invalidate affected cursors.
Example fix
// before: keep paging from a cursor that may be dead
const next = await get(messagesUrl({ first_id: cursor }));
// after: reset cursor on 'First Message Not Exists'
try {
const next = await get(messagesUrl({ first_id: cursor }));
} catch (e) {
if (e.status === 404) { cursor = null; const next = await get(messagesUrl({})); }
else throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Drop pagination cursor before calling if it looks malformed
function validMessageId(v) {
return typeof v === 'string' && /^[0-9a-fA-F-]{36}$/.test(v);
}
if (!validMessageId(firstId)) firstId = null; Type guard
function isLikelyValidFirstId(value) {
return typeof value === 'string' && /^[0-9a-fA-F-]{36}$/.test(value);
} Try / catch
try {
return await get(messagesUrl(id, { first_id: cursor }));
} catch (e) {
if (e.status === 404 && /First Message Not Exists/.test(e.message)) {
cursor = null;
return get(messagesUrl(id, {})); // first page
}
throw e;
} Prevention
- Reset the cursor on 404 and re-fetch the first page.
- Invalidate cursors when message-deletion events arrive.
- Encode first_id exactly as returned (full UUID, no truncation).
When it happens
Trigger: GET messages with ?first_id=<mid> where <mid> is not a valid message id within the conversation, has been deleted, or belongs to a different conversation. Used as the cursor for infinite-scroll pagination backward/forward.
Common situations: Pagination cursor from a stale page; message was deleted between page fetches; first_id concatenated/encoded incorrectly; user jumped to a deep link whose anchor message is gone.
Related errors
- Conversation Not Exists.
- Message Not Exists.
- Recommended app not found
- Installed app not found
- app_unavailable
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/583e7a3586eef0e4.
Report an issue: GitHub.