Files
gh-resolve-io-prism/skills/jira/reference/error-handling.md
2025-11-30 08:51:34 +08:00

16 KiB
Raw Permalink Blame History

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:

❌ 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:

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:

❌ 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:

❌ 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:

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:

❌ 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:

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:

❌ 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:

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:

⏱️ 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:

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:

⚠️ 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:

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:

⚠️ 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:

⏱️ 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:

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:

❌ 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:

❌ 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:

if (!config.jira || !config.jira.enabled) {
  // Jira integration disabled or not configured
}

User Message:

 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:

❌ 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:

if (!process.env.JIRA_EMAIL || !process.env.JIRA_API_TOKEN) {
  // Credentials not configured
}

User Message:

❌ 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:

const issueKeyPattern = /^[A-Z]+-\d+$/;
if (!issueKeyPattern.test(issueKey)) {
  // Invalid format
}

User Message:

❌ 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:

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:

## [PLAT-123] Story Title

...
- **Story Points**: Not set
...

Error Recovery Strategies

1. Retry with Exponential Backoff

For transient errors (429, 500, 503):

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:

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:

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:

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:

✅ 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):

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:

function trackJiraError(errorType) {
  // Increment error counter
  // Store in session metrics
  // Help identify systemic issues
}

Testing Error Scenarios

Manual Testing

Test each error condition:

# 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:

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