{"record":{"id":"e6b1e07d336230d3","repo":"infiniflow/ragflow","slug":"jira-credentials-must-include-either-an-api-token","errorCode":null,"errorMessage":"Jira credentials must include either an API token or username/password.","messagePattern":"Jira credentials must include either an API token or username/password\\.","errorType":"validation","errorClass":"ConnectorMissingCredentialError","httpStatus":null,"severity":"error","filePath":"common/data_source/jira/connector.py","lineNumber":179,"sourceCode":"                self.jira_client = JIRA(\n                    server=jira_url_for_client,\n                    basic_auth=(user_email, api_token),\n                    options=options,\n                )\n            elif api_token:\n                self.jira_client = JIRA(\n                    server=jira_url_for_client,\n                    token_auth=api_token,\n                    options=options,\n                )\n            elif user_email and password:\n                self.jira_client = JIRA(\n                    server=jira_url_for_client,\n                    basic_auth=(user_email, password),\n                    options=options,\n                )\n            else:\n                raise ConnectorMissingCredentialError(\"Jira credentials must include either an API token or username/password.\")\n        except Exception as exc:  # pragma: no cover - jira lib raises many types\n            raise ConnectorMissingCredentialError(f\"Jira: {exc}\") from exc\n        self._sync_timezone_from_server()\n        return None\n\n    def validate_connector_settings(self) -> None:\n        \"\"\"Validate connectivity by fetching basic Jira info.\"\"\"\n        if not self.jira_client:\n            raise ConnectorMissingCredentialError(\"Jira\")\n\n        try:\n            if self.jql_query:\n                dummy_checkpoint = self.build_dummy_checkpoint()\n                checkpoint_callback = self._make_checkpoint_callback(dummy_checkpoint)\n                iterator = self._perform_jql_search(\n                    jql=self.jql_query,\n                    start=0,\n                    max_results=1,","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/jira/connector.py#L161-L197","documentation":"ConnectorMissingCredentialError raised in load_credentials when the credentials dict yields neither an API token (jira_api_token/token/api_token) nor a username+password pair (jira_user_email/jira_username plus jira_password/password). The constructor path was fine; the credential payload itself is incomplete, so no JIRA client could be built.","triggerScenarios":"Calling load_credentials with an empty dict, or with only a username (email but no password/token), or only a password with no username, or a dict using unrecognized key names.","commonSituations":"Frontend credential form submitted with only some fields; the secret store was updated and dropped the token key; confusion between key aliases (using 'apiKey' instead of 'jira_api_token').","solutions":["Provide 'jira_api_token' (preferred) - it alone is sufficient since token_auth is tried first.","Or provide 'jira_user_email' plus 'jira_password' for basic auth against Jira Server.","Check the accepted key aliases in common/data_source/jira/connector.py load_credentials and rename your dict keys to one of them.","Validate required credential keys in your orchestration layer before calling load_credentials."],"exampleFix":"# before\nconnector.load_credentials({'jira_user_email': 'dev@acme.com'})  # -> missing token/password\n\n# after\nconnector.load_credentials({'jira_user_email': 'dev@acme.com', 'jira_api_token': os.environ['JIRA_TOKEN']})","handlingStrategy":"validation","validationCode":"TOKEN_KEYS = ('jira_api_token', 'token', 'api_token')\nUSER_KEYS = ('jira_user_email', 'jira_username')\nPASSWORD_KEYS = ('jira_password', 'password')\n\ndef jira_credentials_sufficient(creds: dict) -> bool:\n    if any(creds.get(k) for k in TOKEN_KEYS):\n        return True\n    return any(creds.get(k) for k in USER_KEYS) and any(creds.get(k) for k in PASSWORD_KEYS)","typeGuard":null,"tryCatchPattern":"try:\n    connector.load_credentials(creds)\nexcept ConnectorMissingCredentialError as e:\n    if 'API token or username/password' in str(e):\n        raise ValueError('Jira credential form incomplete: provide an API token or user+password') from e\n    raise","preventionTips":["Make the API token the canonical credential path; avoid collecting passwords in the UI.","Validate credential key presence in a shared pre-flight before any connector load.","Document accepted key aliases next to the credential form so users copy exact names."],"tags":["jira","credentials","authentication","connector"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}