Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix(atlassian): unify error message extraction across all Jira, JSM, …
…and Confluence routes

Add parseAtlassianErrorMessage() to jira/utils.ts as single source of truth for
parsing all 5 Atlassian error formats. Update 51 proxy routes (18 JSM, 5 Jira,
28 Confluence) to use it instead of hardcoded generic errors. Remove dead
errorExtractor field from 95 Atlassian tool files — the compat loop in
extractErrorMessage() already handles all formats without it. Consolidate
duplicate parseJsmErrorMessage into a re-export from the shared utility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
  • Loading branch information
waleedlatif1 and claude committed Apr 13, 2026
commit a71a23d877e9e3d1e46be6d50c9c543bcfa2c6f7
12 changes: 7 additions & 5 deletions apps/sim/app/api/tools/confluence/attachment/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getConfluenceCloudId } from '@/tools/confluence/utils'
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'

const logger = createLogger('ConfluenceAttachmentAPI')

Expand Down Expand Up @@ -53,15 +54,16 @@ export async function DELETE(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage =
errorData?.message || `Failed to delete Confluence attachment (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

return NextResponse.json({ attachmentId, deleted: true })
Expand Down
12 changes: 7 additions & 5 deletions apps/sim/app/api/tools/confluence/attachments/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getConfluenceCloudId } from '@/tools/confluence/utils'
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'

const logger = createLogger('ConfluenceAttachmentsAPI')

Expand Down Expand Up @@ -64,15 +65,16 @@ export async function GET(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage =
errorData?.message || `Failed to list Confluence attachments (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

const data = await response.json()
Expand Down
56 changes: 35 additions & 21 deletions apps/sim/app/api/tools/confluence/blogposts/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z } from 'zod'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getConfluenceCloudId } from '@/tools/confluence/utils'
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'

const logger = createLogger('ConfluenceBlogPostsAPI')

Expand Down Expand Up @@ -98,14 +99,16 @@ export async function GET(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage = errorData?.message || `Failed to list blog posts (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

const data = await response.json()
Expand Down Expand Up @@ -197,14 +200,16 @@ export async function POST(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage = errorData?.message || `Failed to create blog post (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

const data = await response.json()
Expand Down Expand Up @@ -253,14 +258,16 @@ export async function POST(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage = errorData?.message || `Failed to get blog post (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

const data = await response.json()
Expand Down Expand Up @@ -326,7 +333,10 @@ export async function PUT(request: NextRequest) {
})

if (!currentResponse.ok) {
throw new Error(`Failed to fetch current blog post: ${currentResponse.status}`)
const errorText = await currentResponse.text()
throw new Error(
parseAtlassianErrorMessage(currentResponse.status, currentResponse.statusText, errorText)
)
}

const currentPost = await currentResponse.json()
Expand Down Expand Up @@ -362,14 +372,16 @@ export async function PUT(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage = errorData?.message || `Failed to update blog post (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

const data = await response.json()
Expand Down Expand Up @@ -426,14 +438,16 @@ export async function DELETE(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage = errorData?.message || `Failed to delete blog post (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

return NextResponse.json({ blogPostId, deleted: true })
Expand Down
28 changes: 17 additions & 11 deletions apps/sim/app/api/tools/confluence/comment/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z } from 'zod'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getConfluenceCloudId } from '@/tools/confluence/utils'
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'

const logger = createLogger('ConfluenceCommentAPI')

Expand Down Expand Up @@ -81,7 +82,10 @@ export async function PUT(request: NextRequest) {
})

if (!getResponse.ok) {
throw new Error(`Failed to fetch current comment: ${getResponse.status}`)
const errorText = await getResponse.text()
throw new Error(
parseAtlassianErrorMessage(getResponse.status, getResponse.statusText, errorText)
)
}

const currentComment = await getResponse.json()
Expand Down Expand Up @@ -111,15 +115,16 @@ export async function PUT(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage =
errorData?.message || `Failed to update Confluence comment (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

const data = await response.json()
Expand Down Expand Up @@ -169,15 +174,16 @@ export async function DELETE(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage =
errorData?.message || `Failed to delete Confluence comment (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

return NextResponse.json({ commentId, deleted: true })
Expand Down
23 changes: 13 additions & 10 deletions apps/sim/app/api/tools/confluence/comments/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getConfluenceCloudId } from '@/tools/confluence/utils'
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'

const logger = createLogger('ConfluenceCommentsAPI')

Expand Down Expand Up @@ -69,15 +70,16 @@ export async function POST(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage =
errorData?.message || `Failed to create Confluence comment (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

const data = await response.json()
Expand Down Expand Up @@ -149,15 +151,16 @@ export async function GET(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})
const errorMessage =
errorData?.message || `Failed to list Confluence comments (${response.status})`
return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
}

const data = await response.json()
Expand Down
28 changes: 7 additions & 21 deletions apps/sim/app/api/tools/confluence/create-page/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getConfluenceCloudId } from '@/tools/confluence/utils'
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'

const logger = createLogger('ConfluenceCreatePageAPI')

Expand Down Expand Up @@ -101,31 +102,16 @@ export async function POST(request: NextRequest) {
})

if (!response.ok) {
const errorData = await response.json().catch(() => null)
const errorText = await response.text()
logger.error('Confluence API error response:', {
status: response.status,
statusText: response.statusText,
error: JSON.stringify(errorData, null, 2),
error: errorText,
})

let errorMessage = `Failed to create Confluence page (${response.status})`
if (errorData?.message) {
errorMessage = errorData.message
} else if (errorData?.errors && Array.isArray(errorData.errors)) {
const firstError = errorData.errors[0]
if (firstError?.title) {
if (firstError.title.includes("'spaceId'") && firstError.title.includes('Long')) {
errorMessage =
'Invalid Space ID. Use the list spaces operation to find valid space IDs.'
} else {
errorMessage = firstError.title
}
} else {
errorMessage = JSON.stringify(errorData.errors)
}
}

return NextResponse.json({ error: errorMessage }, { status: response.status })
return NextResponse.json(
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
{ status: response.status }
)
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated
}

const data = await response.json()
Expand Down
Loading
Loading