infiniflow/ragflow · critical · ConnectorMissingCredentialError

Bitbucket

Error message

Bitbucket

What it means

BitbucketConnector.load_credentials raises ConnectorMissingCredentialError('Bitbucket') when either bitbucket_email or bitbucket_api_token is absent/empty from the credentials dict.

Source

Thrown at common/data_source/bitbucket/connector.py:93

        projects: str | None = None,
        batch_size: int = INDEX_BATCH_SIZE,
    ) -> None:
        self.workspace = workspace
        self._repositories = [s.strip() for s in repositories.split(",") if s.strip()] if repositories else None
        self._projects: list[str] | None = [s.strip() for s in projects.split(",") if s.strip()] if projects else None
        self.batch_size = batch_size
        self.email: str | None = None
        self.api_token: str | None = None

    def load_credentials(self, credentials: dict[str, Any]) -> dict[str, Any] | None:
        """Load API token-based credentials.

        Expects a dict with keys: `bitbucket_email`, `bitbucket_api_token`.
        """
        self.email = credentials.get("bitbucket_email")
        self.api_token = credentials.get("bitbucket_api_token")
        if not self.email or not self.api_token:
            raise ConnectorMissingCredentialError("Bitbucket")
        return None

    def _client(self) -> httpx.Client:
        """Build an authenticated HTTP client or raise if credentials missing."""
        if not self.email or not self.api_token:
            raise ConnectorMissingCredentialError("Bitbucket")
        return build_auth_client(self.email, self.api_token)

    def _iter_pull_requests_for_repo(
        self,
        client: httpx.Client,
        repo_slug: str,
        params: dict[str, Any] | None = None,
        start_url: str | None = None,
        on_page: Callable[[str | None], None] | None = None,
    ) -> Iterator[dict[str, Any]]:
        base = f"https://api.bitbucket.org/2.0/repositories/{self.workspace}/{repo_slug}/pullrequests"
        yield from paginate(

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Provide both keys: {'bitbucket_email': '<email>', 'bitbucket_api_token': '<app password>'}
  2. Check for whitespace-only values; the truthiness check fails on empty strings
  3. Use a Bitbucket app password / API token with repository read scope

Example fix

// before
conn.load_credentials({"bitbucket_email": "me@example.com"})

// after
conn.load_credentials({"bitbucket_email": "me@example.com", "bitbucket_api_token": "xxxx"})
Defensive patterns

Strategy: validation

Validate before calling

def valid_bitbucket_creds(creds: dict) -> bool:
    return bool(creds.get("bitbucket_email")) and bool(creds.get("bitbucket_api_token"))

assert valid_bitbucket_creds(creds), "need bitbucket_email and bitbucket_api_token"

Type guard

def is_bitbucket_cred_dict(x) -> bool:
    """True when x carries both required Bitbucket credential fields."""
    return (
        isinstance(x, dict)
        and isinstance(x.get("bitbucket_email"), str) and x["bitbucket_email"].strip() != ""
        and isinstance(x.get("bitbucket_api_token"), str) and x["bitbucket_api_token"].strip() != ""
    )

Try / catch

try:
    conn.load_credentials(creds)
except ConnectorMissingCredentialError:
    creds = prompt_for_bitbucket_credentials()  # refill missing keys
    conn.load_credentials(creds)

Prevention

When it happens

Trigger: Calling load_credentials with a dict missing 'bitbucket_email' or 'bitbucket_api_token', or where either value is an empty string/None.

Common situations: Storing the app password under a different key; blank form fields; secrets manager returning None on a missing entry.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/663a16a1158d9cac. Report an issue: GitHub.