Skip to content

Commit 4375f99

Browse files
waleedlatif1claude
andauthored
fix(atlassian): unify error message extraction across all routes (#4135)
* 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> * fix: address PR review comments from Bugbot - Remove debug logger.info for formAnswers in JSM request route - Restore user-friendly spaceId error message in Confluence create-page route - Restore details field in Jira write and update route error responses Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove re-exports from jsm/utils and import directly from source Remove re-exports of getJiraCloudId, parseAtlassianErrorMessage, and parseJsmErrorMessage from jsm/utils.ts. Update all 21 JSM routes to import directly from @/tools/jira/utils per CLAUDE.md import rules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * regen docs --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fb4fb9e commit 4375f99

File tree

153 files changed

+580
-640
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

153 files changed

+580
-640
lines changed

apps/docs/content/docs/en/tools/jira_service_management.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Create a new service request in Jira Service Management
117117
| `description` | string | No | Description for the service request |
118118
| `raiseOnBehalfOf` | string | No | Account ID of customer to raise request on behalf of |
119119
| `requestFieldValues` | json | No | Request field values as key-value pairs \(overrides summary/description if provided\) |
120-
| `formAnswers` | json | No | Form answers for form-based request types \(e.g., \{"summary": \{"text": "Title"\}, "customfield_10010": \{"choices": \["10320"\]\}\}\) |
120+
| `formAnswers` | json | No | Form answers using numeric form question IDs as keys \(e.g., \{"1": \{"text": "Title"\}, "4": \{"choices": \["5"\]\}\}\). Keys are question IDs from the Jira Form, not Jira field names. |
121121
| `requestParticipants` | string | No | Comma-separated account IDs to add as request participants |
122122
| `channel` | string | No | Channel the request originates from \(e.g., portal, email\) |
123123

apps/sim/app/api/tools/confluence/attachment/route.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
33
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
44
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
55
import { getConfluenceCloudId } from '@/tools/confluence/utils'
6+
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'
67

78
const logger = createLogger('ConfluenceAttachmentAPI')
89

@@ -53,15 +54,16 @@ export async function DELETE(request: NextRequest) {
5354
})
5455

5556
if (!response.ok) {
56-
const errorData = await response.json().catch(() => null)
57+
const errorText = await response.text()
5758
logger.error('Confluence API error response:', {
5859
status: response.status,
5960
statusText: response.statusText,
60-
error: JSON.stringify(errorData, null, 2),
61+
error: errorText,
6162
})
62-
const errorMessage =
63-
errorData?.message || `Failed to delete Confluence attachment (${response.status})`
64-
return NextResponse.json({ error: errorMessage }, { status: response.status })
63+
return NextResponse.json(
64+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
65+
{ status: response.status }
66+
)
6567
}
6668

6769
return NextResponse.json({ attachmentId, deleted: true })

apps/sim/app/api/tools/confluence/attachments/route.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
33
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
44
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
55
import { getConfluenceCloudId } from '@/tools/confluence/utils'
6+
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'
67

78
const logger = createLogger('ConfluenceAttachmentsAPI')
89

@@ -64,15 +65,16 @@ export async function GET(request: NextRequest) {
6465
})
6566

6667
if (!response.ok) {
67-
const errorData = await response.json().catch(() => null)
68+
const errorText = await response.text()
6869
logger.error('Confluence API error response:', {
6970
status: response.status,
7071
statusText: response.statusText,
71-
error: JSON.stringify(errorData, null, 2),
72+
error: errorText,
7273
})
73-
const errorMessage =
74-
errorData?.message || `Failed to list Confluence attachments (${response.status})`
75-
return NextResponse.json({ error: errorMessage }, { status: response.status })
74+
return NextResponse.json(
75+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
76+
{ status: response.status }
77+
)
7678
}
7779

7880
const data = await response.json()

apps/sim/app/api/tools/confluence/blogposts/route.ts

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { z } from 'zod'
44
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
55
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
66
import { getConfluenceCloudId } from '@/tools/confluence/utils'
7+
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'
78

89
const logger = createLogger('ConfluenceBlogPostsAPI')
910

@@ -98,14 +99,16 @@ export async function GET(request: NextRequest) {
9899
})
99100

100101
if (!response.ok) {
101-
const errorData = await response.json().catch(() => null)
102+
const errorText = await response.text()
102103
logger.error('Confluence API error response:', {
103104
status: response.status,
104105
statusText: response.statusText,
105-
error: JSON.stringify(errorData, null, 2),
106+
error: errorText,
106107
})
107-
const errorMessage = errorData?.message || `Failed to list blog posts (${response.status})`
108-
return NextResponse.json({ error: errorMessage }, { status: response.status })
108+
return NextResponse.json(
109+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
110+
{ status: response.status }
111+
)
109112
}
110113

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

199202
if (!response.ok) {
200-
const errorData = await response.json().catch(() => null)
203+
const errorText = await response.text()
201204
logger.error('Confluence API error response:', {
202205
status: response.status,
203206
statusText: response.statusText,
204-
error: JSON.stringify(errorData, null, 2),
207+
error: errorText,
205208
})
206-
const errorMessage = errorData?.message || `Failed to create blog post (${response.status})`
207-
return NextResponse.json({ error: errorMessage }, { status: response.status })
209+
return NextResponse.json(
210+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
211+
{ status: response.status }
212+
)
208213
}
209214

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

255260
if (!response.ok) {
256-
const errorData = await response.json().catch(() => null)
261+
const errorText = await response.text()
257262
logger.error('Confluence API error response:', {
258263
status: response.status,
259264
statusText: response.statusText,
260-
error: JSON.stringify(errorData, null, 2),
265+
error: errorText,
261266
})
262-
const errorMessage = errorData?.message || `Failed to get blog post (${response.status})`
263-
return NextResponse.json({ error: errorMessage }, { status: response.status })
267+
return NextResponse.json(
268+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
269+
{ status: response.status }
270+
)
264271
}
265272

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

328335
if (!currentResponse.ok) {
329-
throw new Error(`Failed to fetch current blog post: ${currentResponse.status}`)
336+
const errorText = await currentResponse.text()
337+
throw new Error(
338+
parseAtlassianErrorMessage(currentResponse.status, currentResponse.statusText, errorText)
339+
)
330340
}
331341

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

364374
if (!response.ok) {
365-
const errorData = await response.json().catch(() => null)
375+
const errorText = await response.text()
366376
logger.error('Confluence API error response:', {
367377
status: response.status,
368378
statusText: response.statusText,
369-
error: JSON.stringify(errorData, null, 2),
379+
error: errorText,
370380
})
371-
const errorMessage = errorData?.message || `Failed to update blog post (${response.status})`
372-
return NextResponse.json({ error: errorMessage }, { status: response.status })
381+
return NextResponse.json(
382+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
383+
{ status: response.status }
384+
)
373385
}
374386

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

428440
if (!response.ok) {
429-
const errorData = await response.json().catch(() => null)
441+
const errorText = await response.text()
430442
logger.error('Confluence API error response:', {
431443
status: response.status,
432444
statusText: response.statusText,
433-
error: JSON.stringify(errorData, null, 2),
445+
error: errorText,
434446
})
435-
const errorMessage = errorData?.message || `Failed to delete blog post (${response.status})`
436-
return NextResponse.json({ error: errorMessage }, { status: response.status })
447+
return NextResponse.json(
448+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
449+
{ status: response.status }
450+
)
437451
}
438452

439453
return NextResponse.json({ blogPostId, deleted: true })

apps/sim/app/api/tools/confluence/comment/route.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { z } from 'zod'
44
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
55
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
66
import { getConfluenceCloudId } from '@/tools/confluence/utils'
7+
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'
78

89
const logger = createLogger('ConfluenceCommentAPI')
910

@@ -81,7 +82,10 @@ export async function PUT(request: NextRequest) {
8182
})
8283

8384
if (!getResponse.ok) {
84-
throw new Error(`Failed to fetch current comment: ${getResponse.status}`)
85+
const errorText = await getResponse.text()
86+
throw new Error(
87+
parseAtlassianErrorMessage(getResponse.status, getResponse.statusText, errorText)
88+
)
8589
}
8690

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

113117
if (!response.ok) {
114-
const errorData = await response.json().catch(() => null)
118+
const errorText = await response.text()
115119
logger.error('Confluence API error response:', {
116120
status: response.status,
117121
statusText: response.statusText,
118-
error: JSON.stringify(errorData, null, 2),
122+
error: errorText,
119123
})
120-
const errorMessage =
121-
errorData?.message || `Failed to update Confluence comment (${response.status})`
122-
return NextResponse.json({ error: errorMessage }, { status: response.status })
124+
return NextResponse.json(
125+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
126+
{ status: response.status }
127+
)
123128
}
124129

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

171176
if (!response.ok) {
172-
const errorData = await response.json().catch(() => null)
177+
const errorText = await response.text()
173178
logger.error('Confluence API error response:', {
174179
status: response.status,
175180
statusText: response.statusText,
176-
error: JSON.stringify(errorData, null, 2),
181+
error: errorText,
177182
})
178-
const errorMessage =
179-
errorData?.message || `Failed to delete Confluence comment (${response.status})`
180-
return NextResponse.json({ error: errorMessage }, { status: response.status })
183+
return NextResponse.json(
184+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
185+
{ status: response.status }
186+
)
181187
}
182188

183189
return NextResponse.json({ commentId, deleted: true })

apps/sim/app/api/tools/confluence/comments/route.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
33
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
44
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
55
import { getConfluenceCloudId } from '@/tools/confluence/utils'
6+
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'
67

78
const logger = createLogger('ConfluenceCommentsAPI')
89

@@ -69,15 +70,16 @@ export async function POST(request: NextRequest) {
6970
})
7071

7172
if (!response.ok) {
72-
const errorData = await response.json().catch(() => null)
73+
const errorText = await response.text()
7374
logger.error('Confluence API error response:', {
7475
status: response.status,
7576
statusText: response.statusText,
76-
error: JSON.stringify(errorData, null, 2),
77+
error: errorText,
7778
})
78-
const errorMessage =
79-
errorData?.message || `Failed to create Confluence comment (${response.status})`
80-
return NextResponse.json({ error: errorMessage }, { status: response.status })
79+
return NextResponse.json(
80+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
81+
{ status: response.status }
82+
)
8183
}
8284

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

151153
if (!response.ok) {
152-
const errorData = await response.json().catch(() => null)
154+
const errorText = await response.text()
153155
logger.error('Confluence API error response:', {
154156
status: response.status,
155157
statusText: response.statusText,
156-
error: JSON.stringify(errorData, null, 2),
158+
error: errorText,
157159
})
158-
const errorMessage =
159-
errorData?.message || `Failed to list Confluence comments (${response.status})`
160-
return NextResponse.json({ error: errorMessage }, { status: response.status })
160+
return NextResponse.json(
161+
{ error: parseAtlassianErrorMessage(response.status, response.statusText, errorText) },
162+
{ status: response.status }
163+
)
161164
}
162165

163166
const data = await response.json()

apps/sim/app/api/tools/confluence/create-page/route.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
33
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
44
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
55
import { getConfluenceCloudId } from '@/tools/confluence/utils'
6+
import { parseAtlassianErrorMessage } from '@/tools/jira/utils'
67

78
const logger = createLogger('ConfluenceCreatePageAPI')
89

@@ -101,30 +102,16 @@ export async function POST(request: NextRequest) {
101102
})
102103

103104
if (!response.ok) {
104-
const errorData = await response.json().catch(() => null)
105+
const errorText = await response.text()
105106
logger.error('Confluence API error response:', {
106107
status: response.status,
107108
statusText: response.statusText,
108-
error: JSON.stringify(errorData, null, 2),
109+
error: errorText,
109110
})
110-
111-
let errorMessage = `Failed to create Confluence page (${response.status})`
112-
if (errorData?.message) {
113-
errorMessage = errorData.message
114-
} else if (errorData?.errors && Array.isArray(errorData.errors)) {
115-
const firstError = errorData.errors[0]
116-
if (firstError?.title) {
117-
if (firstError.title.includes("'spaceId'") && firstError.title.includes('Long')) {
118-
errorMessage =
119-
'Invalid Space ID. Use the list spaces operation to find valid space IDs.'
120-
} else {
121-
errorMessage = firstError.title
122-
}
123-
} else {
124-
errorMessage = JSON.stringify(errorData.errors)
125-
}
111+
let errorMessage = parseAtlassianErrorMessage(response.status, response.statusText, errorText)
112+
if (errorMessage.includes("'spaceId'") && errorMessage.includes('Long')) {
113+
errorMessage = 'Invalid Space ID. Use the list spaces operation to find valid space IDs.'
126114
}
127-
128115
return NextResponse.json({ error: errorMessage }, { status: response.status })
129116
}
130117

0 commit comments

Comments
 (0)