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

  1. On 404 with this message, discard the stored first_id cursor and re-fetch the first page.
  2. Encode first_id exactly as returned by the previous response (UUID string, no truncation).
  3. If deep-linking, validate the message id is still present before scrolling to it.
  4. 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

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


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/583e7a3586eef0e4. Report an issue: GitHub.