# Jira Error Handling Guide ## Overview This document provides comprehensive guidance on handling errors when integrating with Jira REST API, including error detection, graceful degradation, and user communication. ## Error Handling Principles ### 1. Graceful Degradation **Never halt the entire workflow** due to Jira issues: - Inform user of Jira unavailability - Offer to proceed without Jira context - Log error details for troubleshooting - Continue with requested task when possible ### 2. User-Friendly Messages **Avoid technical jargon** in user-facing messages: - ❌ "HTTP 403 Forbidden - Insufficient scopes" - ✅ "Access denied to PLAT-123. Please check Jira permissions." ### 3. Actionable Guidance **Tell users what to do next**: - Verify issue key spelling - Check Jira permissions - Contact Jira admin - Proceed without Jira context - Retry after waiting ### 4. Privacy & Security **Never expose sensitive details**: - Don't show API tokens in error messages - Don't log credentials in debug output - Don't reveal internal system details to users ## HTTP Status Codes ### 400 Bad Request **Meaning**: Invalid request syntax or parameters **Common Causes**: - Invalid JQL syntax in search queries - Malformed JSON in request body (not applicable for read-only) - Invalid issue key format **Example Scenarios**: **Invalid JQL**: ``` URL: /rest/api/3/search?jql=invalid syntax here Error: JQL query is invalid ``` **User Message**: ```markdown ❌ Invalid search query. Please check your JQL syntax. You searched for: "invalid syntax here" Common JQL format: - project = PLAT AND type = Bug - assignee = currentUser() - status != Done ``` **Handling Code**: ```javascript if (response.status === 400) { displayMessage(` Invalid Jira search query. The JQL syntax may be incorrect. Would you like to: 1. Try a simpler search 2. View JQL examples 3. Proceed without searching `); // Offer alternatives, don't halt } ``` ### 401 Unauthorized **Meaning**: Missing, invalid, or expired credentials **Common Causes**: - API token not set in environment - Incorrect email address - Expired or revoked API token - Using password instead of API token **Example Scenarios**: **Missing Credentials**: ``` Error: JIRA_EMAIL or JIRA_API_TOKEN not found in environment ``` **User Message**: ```markdown ❌ Jira integration not configured. To enable Jira integration: 1. Generate API token: https://id.atlassian.com/manage-profile/security/api-tokens 2. Add to .env file: ``` JIRA_EMAIL=your.email@resolve.io JIRA_API_TOKEN=your_token_here ``` 3. Restart your terminal/IDE For now, I'll proceed without Jira context. ``` **Invalid Credentials**: ``` Error: HTTP 401 from Jira API ``` **User Message**: ```markdown ❌ Jira authentication failed. Your credentials may be incorrect or expired. Please verify: - JIRA_EMAIL matches your Atlassian account email - JIRA_API_TOKEN is a valid, active API token - Token hasn't been revoked Generate new token: https://id.atlassian.com/manage-profile/security/api-tokens Proceeding without Jira context for now. ``` **Handling Code**: ```javascript if (response.status === 401) { // Check if credentials are configured at all if (!hasJiraCredentials()) { displayMessage("Jira integration not configured. See setup instructions."); } else { displayMessage("Jira authentication failed. Please verify your credentials."); } // Continue workflow without Jira return null; // Indicate no Jira data available } ``` ### 403 Forbidden **Meaning**: Authenticated but lacks permission **Common Causes**: - User lacks permission to view issue - Issue in restricted project - User lacks Jira license - Project permissions changed **Example Scenarios**: **Issue in Restricted Project**: ``` Error: You do not have permission to view this issue Issue: PLAT-123 ``` **User Message**: ```markdown ❌ Access denied to [PLAT-123](https://resolvesys.atlassian.net/browse/PLAT-123). This could mean: - The issue is in a restricted project - You don't have permission to view this issue - The project permissions recently changed Please: - Verify you can access the issue in Jira web UI - Request access from your Jira administrator - Double-check the issue key Would you like to proceed without this issue's context? ``` **Handling Code**: ```javascript if (response.status === 403) { displayMessage(` Access denied to ${issueKey}. You may not have permission to view this issue. You can still view it in Jira web UI if accessible: ${jiraUrl}/browse/${issueKey} Proceed without Jira context? (y/n) `); // Wait for user decision // Continue without Jira or halt if user requests } ``` ### 404 Not Found **Meaning**: Issue does not exist **Common Causes**: - Typo in issue key - Issue was deleted - Issue moved to different project (key changed) - Wrong Jira instance **Example Scenarios**: **Non-Existent Issue**: ``` Error: Issue does not exist Issue: PLAT-9999 ``` **User Message**: ```markdown ❌ Could not find Jira issue [PLAT-9999](https://resolvesys.atlassian.net/browse/PLAT-9999). Possible reasons: - Typo in issue key (check spelling and number) - Issue was deleted or moved - Issue is in a different project Would you like to: 1. Search for similar issues 2. Verify the issue key 3. Proceed without Jira context ``` **Typo Detection**: ```javascript if (response.status === 404) { // Suggest likely corrections const suggestions = findSimilarIssueKeys(issueKey); displayMessage(` Issue ${issueKey} not found. Did you mean: ${suggestions.map(s => `- ${s}`).join('\n')} Or proceed without Jira context? `); } ``` ### 429 Too Many Requests **Meaning**: Rate limit exceeded **Common Causes**: - Exceeded 300 requests per minute (per user) - Multiple rapid fetches in short time - Shared API token hitting combined limits **Example Scenarios**: **Rate Limit Hit**: ``` Error: Rate limit exceeded Retry-After: 60 seconds ``` **User Message**: ```markdown ⏱️ Jira rate limit reached. Please wait a moment. Jira Cloud limits requests to 300 per minute per user. I'll automatically retry in 60 seconds, or you can: - Wait and try again manually - Proceed without fetching additional Jira issues - Use cached issue data if available ``` **Handling Code**: ```javascript if (response.status === 429) { const retryAfter = response.headers['Retry-After'] || 60; displayMessage(` Jira rate limit exceeded. Waiting ${retryAfter} seconds before retry... `); // Implement exponential backoff await sleep(retryAfter * 1000); // Retry request return retryRequest(url, maxRetries - 1); } ``` **Prevention**: - Cache fetched issues for conversation session - Batch operations when possible - Avoid fetching same issue multiple times - Use search queries to fetch multiple issues at once ### 500 Internal Server Error **Meaning**: Jira service error **Common Causes**: - Jira service temporarily down - Database issues on Jira side - Unexpected server error - Maintenance window **Example Scenarios**: **Service Outage**: ``` Error: HTTP 500 Internal Server Error ``` **User Message**: ```markdown ⚠️ Jira service error. The Jira server may be temporarily unavailable. This is typically a temporary issue on Jira's side. You can: - Check Jira status: https://status.atlassian.com/ - Retry in a few minutes - Proceed without Jira context for now I'll continue with your request using available information. ``` **Handling Code**: ```javascript if (response.status >= 500) { displayMessage(` Jira service is temporarily unavailable. Check status: https://status.atlassian.com/ Proceeding without Jira context. `); // Log error for troubleshooting logError('Jira 500 error', { issueKey, timestamp: new Date() }); // Continue workflow without Jira return null; } ``` ### 502/503/504 Gateway/Service Errors **Meaning**: Jira proxy or gateway issues **Common Causes**: - Network connectivity issues - Jira proxy/gateway timeout - Temporary service degradation **User Message**: ```markdown ⚠️ Unable to reach Jira. Network or service issue detected. This is usually temporary. You can: - Retry in a few moments - Check your network connection - Check Jira status: https://status.atlassian.com/ - Proceed without Jira context Continuing with available information... ``` ## Network Errors ### Connection Timeout **Meaning**: Request took too long to complete **User Message**: ```markdown ⏱️ Jira request timed out. The service may be slow or unreachable. You can: - Retry the request - Check your network connection - Proceed without Jira context Continuing without Jira data... ``` **Handling Code**: ```javascript try { const response = await fetchWithTimeout(url, 30000); // 30s timeout } catch (error) { if (error.name === 'TimeoutError') { displayMessage('Jira request timed out. Proceeding without Jira context.'); return null; } throw error; } ``` ### Connection Refused **Meaning**: Cannot connect to Jira server **User Message**: ```markdown ❌ Cannot connect to Jira. Network issue detected. Please check: - Your internet connection - VPN connection (if required) - Firewall settings - Jira base URL in core-config.yaml Proceeding without Jira integration. ``` ### DNS Resolution Failed **Meaning**: Cannot resolve Jira hostname **User Message**: ```markdown ❌ Cannot resolve Jira hostname. Please verify: - Jira base URL in core-config.yaml: https://resolvesys.atlassian.net - DNS settings - Network connectivity Proceeding without Jira context. ``` ## Configuration Errors ### Missing Configuration **Scenario**: Jira not configured in core-config.yaml **Detection**: ```javascript if (!config.jira || !config.jira.enabled) { // Jira integration disabled or not configured } ``` **User Message**: ```markdown ℹ️ Jira integration is not enabled. To enable: 1. Set `jira.enabled: true` in core-config.yaml 2. Configure JIRA_EMAIL and JIRA_API_TOKEN in .env 3. See: .prism/skills/jira/reference/authentication.md Proceeding without Jira integration. ``` ### Invalid Base URL **Scenario**: Malformed Jira base URL **User Message**: ```markdown ❌ Invalid Jira base URL in configuration. Current: {current_url} Expected format: https://your-domain.atlassian.net Please correct in core-config.yaml. ``` ### Missing Environment Variables **Scenario**: JIRA_EMAIL or JIRA_API_TOKEN not set **Detection**: ```javascript if (!process.env.JIRA_EMAIL || !process.env.JIRA_API_TOKEN) { // Credentials not configured } ``` **User Message**: ```markdown ❌ Jira credentials not configured. Required environment variables: - JIRA_EMAIL - JIRA_API_TOKEN Setup instructions: .prism/skills/jira/reference/authentication.md Proceeding without Jira integration. ``` ## Issue-Specific Errors ### Invalid Issue Key Format **Scenario**: Issue key doesn't match expected pattern **Detection**: ```javascript const issueKeyPattern = /^[A-Z]+-\d+$/; if (!issueKeyPattern.test(issueKey)) { // Invalid format } ``` **User Message**: ```markdown ❌ Invalid issue key format: "{issueKey}" Jira issue keys follow this pattern: - PROJECT-123 - ABC-456 - PLAT-789 Format: {PROJECT_KEY}-{NUMBER} All letters uppercase, hyphen, then numbers. ``` ### Custom Field Not Found **Scenario**: Expected custom field missing from issue **Handling**: ```javascript const storyPoints = issue.fields.customfield_10016 || issue.fields.storyPoints || null; if (!storyPoints) { // Handle gracefully - don't error, just note missing displayField('Story Points', 'Not set'); } ``` **User Message**: ```markdown ## [PLAT-123] Story Title ... - **Story Points**: Not set ... ``` ## Error Recovery Strategies ### 1. Retry with Exponential Backoff For transient errors (429, 500, 503): ```javascript async function fetchWithRetry(url, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const response = await fetch(url); if (response.ok) { return response; } // Retry on 429, 500, 503 if ([429, 500, 503].includes(response.status)) { const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s await sleep(delay); continue; } // Don't retry on 401, 403, 404 return response; } catch (error) { if (i === maxRetries - 1) throw error; await sleep(Math.pow(2, i) * 1000); } } } ``` ### 2. Fallback to Cached Data If fresh fetch fails, use cached data: ```javascript try { const freshData = await fetchIssue(issueKey); cacheIssue(issueKey, freshData); return freshData; } catch (error) { const cached = getCachedIssue(issueKey); if (cached) { displayMessage('⚠️ Using cached issue data (Jira temporarily unavailable)'); return cached; } // No cached data, proceed without Jira return null; } ``` ### 3. Partial Success For batch operations, handle individual failures: ```javascript async function fetchMultipleIssues(issueKeys) { const results = []; const failed = []; for (const key of issueKeys) { try { const issue = await fetchIssue(key); results.push(issue); } catch (error) { failed.push({ key, error: error.message }); } } if (failed.length > 0) { displayMessage(` ⚠️ Failed to fetch ${failed.length} issues: ${failed.map(f => `- ${f.key}: ${f.error}`).join('\n')} Continuing with ${results.length} successfully fetched issues. `); } return results; } ``` ### 4. Degrade Feature If Jira unavailable, continue with reduced functionality: ```javascript if (!jiraAvailable) { displayMessage(` ℹ️ Jira integration unavailable. Continuing with limited context. Please provide issue details manually if needed: - Summary - Acceptance Criteria - Technical requirements `); // Continue workflow, prompt user for manual input return promptForManualIssueDetails(); } ``` ## Logging & Debugging ### User-Facing Messages **Keep concise and actionable**: ```markdown ✅ Good: "Access denied to PLAT-123. Please check permissions." ❌ Bad: "HTTPError: 403 Forbidden - insufficient_scope - user lacks jira.issue.read" ``` ### Debug Logging **Log details for troubleshooting** (not shown to user): ```javascript function logJiraError(error, context) { console.error('[Jira Integration Error]', { timestamp: new Date().toISOString(), issueKey: context.issueKey, url: context.url, status: error.status, message: error.message, // Never log credentials! }); } ``` ### Error Telemetry **Track error patterns** for improvement: ```javascript function trackJiraError(errorType) { // Increment error counter // Store in session metrics // Help identify systemic issues } ``` ## Testing Error Scenarios ### Manual Testing Test each error condition: ```bash # 401 - Invalid credentials JIRA_EMAIL=wrong@email.com JIRA_API_TOKEN=invalid jira PLAT-123 # 403 - Access denied (use issue you don't have access to) jira RESTRICTED-999 # 404 - Not found jira PLAT-99999999 # Invalid format jira plat-123 jira PLAT123 jira 123 ``` ### Automated Testing Mock error responses: ```javascript describe('Jira Error Handling', () => { test('401 shows auth help message', async () => { mockFetch.mockResolvedValue({ status: 401 }); const result = await fetchIssue('PLAT-123'); expect(result).toBeNull(); expect(displayedMessage).toContain('authentication failed'); }); test('404 offers to search', async () => { mockFetch.mockResolvedValue({ status: 404 }); await fetchIssue('PLAT-999'); expect(displayedMessage).toContain('search for similar issues'); }); }); ``` ## Best Practices Summary ### ✅ DO - Provide clear, actionable error messages - Degrade gracefully (never halt entire workflow) - Offer alternatives (search, manual input, proceed without) - Cache data to reduce API calls and handle failures - Log errors for debugging (without exposing credentials) - Retry transient errors with exponential backoff - Respect rate limits ### ❌ DON'T - Expose API tokens or credentials in errors - Show technical jargon to users - Halt workflow on Jira errors - Retry non-transient errors (401, 403, 404) - Ignore errors silently - Spam API with rapid retries - Assume Jira is always available ## References - [Jira REST API Errors](https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/#error-responses) - [HTTP Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) - [Atlassian Status Page](https://status.atlassian.com/)