langgenius/dify · warning · BadRequest

Invalid cursor

Error message

Invalid cursor

What it means

BadRequest (HTTP 400, werkzeug) with message 'Invalid cursor' is raised in _decode_installed_app_cursor when the base64url cursor query parameter cannot be decoded or validated as an InstalledAppCursor. The cursor is an opaque, base64url-encoded JSON blob produced by _encode_installed_app_cursor; any tampering, truncation, or format mismatch triggers this error.

Source

Thrown at api/controllers/console/explore/installed_app.py:79

        return None
    return file_helpers.get_signed_file_url(icon)


def _encode_installed_app_cursor(cursor: InstalledAppCursor) -> str:
    payload = cursor.model_dump_json().encode()
    return base64.urlsafe_b64encode(payload).decode().rstrip("=")


def _decode_installed_app_cursor(cursor: str | None) -> InstalledAppCursor | None:
    if cursor is None:
        return None

    try:
        padded_cursor = cursor + "=" * (-len(cursor) % 4)
        payload = base64.b64decode(padded_cursor, altchars=b"-_", validate=True)
        return InstalledAppCursor.model_validate_json(payload)
    except (binascii.Error, UnicodeDecodeError, ValueError):
        raise BadRequest("Invalid cursor") from None


class InstalledAppInfoResponse(ResponseModel):
    id: str
    name: str
    description: str
    mode: AppMode
    icon_type: IconType | None
    icon: str | None
    icon_background: str | None
    use_icon_as_answer_icon: bool

    @computed_field(return_type=str | None)  # type: ignore[prop-decorator]
    @property
    def icon_url(self) -> str | None:
        return _build_icon_url(self.icon_type, self.icon)

View on GitHub (pinned to ef8544b173)

Solutions

  1. Restart pagination by omitting the cursor parameter entirely — this fetches the first page and returns a fresh next_cursor.
  2. Ensure the client passes the next_cursor value from the API response verbatim, without URL-decoding, truncation, or modification.
  3. If the cursor is being logged or displayed, ensure it is not truncated by tooling before being reused.

Example fix

// before: cursor truncated or mangled
GET /installed-apps?cursor=eyJsaW1pdCI6Mj
// after: restart pagination without cursor, then use the returned next_cursor verbatim
GET /installed-apps?limit=20
// then: GET /installed-apps?cursor=<exact next_cursor from response>
Defensive patterns

Strategy: validation

Validate before calling

function isValidCursor(cursor) {
  if (!cursor) return true; // null/undefined is valid (first page)
  try {
    const padded = cursor + '='.repeat((4 - cursor.length % 4) % 4);
    const decoded = atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
    JSON.parse(decoded); // must be valid JSON
    return true;
  } catch {
    return false;
  }
}
// before calling the list endpoint:
if (!isValidCursor(cursor)) cursor = null; // restart from page 1

Type guard

function isValidInstalledAppCursor(cursor) {
  if (!cursor) return true;
  try {
    const padded = cursor + '='.repeat((4 - cursor.length % 4) % 4);
    const decoded = atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
    const parsed = JSON.parse(decoded);
    return typeof parsed === 'object' && parsed !== null;
  } catch {
    return false;
  }
}

Try / catch

try {
  const page = await listInstalledApps(cursor);
} catch (e) {
  if (e.status === 400 && e.message?.includes('Invalid cursor')) {
    // cursor is corrupt — restart pagination from the first page
    const freshPage = await listInstalledApps(null);
    return freshPage;
  }
  throw e;
}

Prevention

When it happens

Trigger: GET /console/explore/installed-apps?cursor=<malformed> where the cursor value is not valid base64url, has been truncated or manually edited, decodes to non-UTF-8 bytes, or fails Pydantic validation against the InstalledAppCursor schema.

Common situations: The cursor was truncated by a URL shortener or log truncation; the cursor was manually constructed or edited; a client bug stored a truncated or URL-decoded version of the cursor; the cursor was passed through a system that mangled the base64url characters (- and _).

Related errors


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