{"record":{"id":"34dd4365a8be9037","repo":"infiniflow/ragflow","slug":"provide-either-an-api-token-or-both-email-password","errorCode":null,"errorMessage":"Provide either an API token or both email/password for Jira authentication.","messagePattern":"Provide either an API token or both email/password for Jira authentication\\.","errorType":"validation","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"common/data_source/jira/connector.py","lineNumber":962,"sourceCode":"            \"start_ts\": args.start_ts,\n            \"end_ts\": args.end_ts,\n            \"include_comments\": args.include_comments,\n            \"include_attachments\": args.include_attachments,\n            \"attachment_size_limit\": args.attachment_size_limit,\n            \"credentials\": {\n                \"jira_user_email\": args.user_email,\n                \"jira_api_token\": args.api_token,\n                \"jira_password\": args.password,\n            },\n        }\n\n    base_url = config.get(\"base_url\")\n    credentials = config.get(\"credentials\", {})\n\n    if not base_url:\n        raise RuntimeError(\"Jira base URL must be provided via config or CLI arguments.\")\n    if not (credentials.get(\"jira_api_token\") or ((credentials.get(\"jira_user_email\") or credentials.get(\"jira_username\")) and credentials.get(\"jira_password\"))):\n        raise RuntimeError(\"Provide either an API token or both email/password for Jira authentication.\")\n\n    connector_options = {\n        key: value\n        for key, value in (\n            (\"include_comments\", config.get(\"include_comments\")),\n            (\"include_attachments\", config.get(\"include_attachments\")),\n            (\"attachment_size_limit\", config.get(\"attachment_size_limit\")),\n            (\"labels_to_skip\", config.get(\"labels_to_skip\")),\n            (\"comment_email_blacklist\", config.get(\"comment_email_blacklist\")),\n            (\"scoped_token\", config.get(\"scoped_token\")),\n            (\"timezone_offset\", config.get(\"timezone_offset\")),\n        )\n        if value is not None\n    }\n\n    documents = test_jira(\n        base_url=base_url,\n        project_key=config.get(\"project_key\"),","sourceCodeStart":944,"sourceCodeEnd":980,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/jira/connector.py#L944-L980","documentation":"A RuntimeError from the test/CLI entrypoint validating the credentials dict: it requires either jira_api_token, or the pair (jira_user_email or jira_username) plus jira_password. The Jira client supports two auth schemes and this check rejects credentials that satisfy neither before any network call is made.","triggerScenarios":"Running the test/CLI flow where credentials contain only a password without a user identity, only an email with no token/password, or nothing at all — e.g. passing --api-token as an empty string while omitting --user-email/--password.","commonSituations":"Confusing Jira Cloud (email + API token) with Data Center (username + password) auth and passing a mix; empty-string env vars expanding into the args; rotating to scoped tokens without setting jira_api_token; shell quoting dropping the value.","solutions":["For Jira Cloud supply --user-email and --api-token (a token created at id.atlassian.com/manage-profile/security/api-tokens).","For Data Center/self-hosted with password auth supply username (or email) AND password together.","Print/inspect the credentials keys (never the values) right before the call to confirm none are empty strings — empty strings fail the truthiness check.","If using scoped tokens, ensure they flow into jira_api_token (or scoped_token option) rather than a password field."],"exampleFix":"# before\nconfig['credentials'] = {'jira_user_email': 'you@org.com'}  # RuntimeError: Provide either an API token or both email/password\n\n# after\nconfig['credentials'] = {'jira_user_email': 'you@org.com', 'jira_api_token': os.environ['JIRA_API_TOKEN']}","handlingStrategy":"validation","validationCode":"creds = config.get(\"credentials\", {})\nhas_token = bool(creds.get(\"jira_api_token\"))\nhas_password_pair = bool((creds.get(\"jira_user_email\") or creds.get(\"jira_username\")) and creds.get(\"jira_password\"))\nif not (has_token or has_password_pair):\n    raise ValueError(\"need jira_api_token, or (jira_user_email|jira_username)+jira_password\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Decide the auth scheme up front: Cloud = email+token, Data Center = username+password; never mix.","Reject empty-string credential values during config parsing so truthiness checks behave.","Load secrets from env/secret store, not CLI history."],"tags":["jira","authentication","cli","configuration","credentials"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}