fix(attachments): make --replace work on Data Center/Server - #257
Conversation
`attachment-upload --replace` sent PUT /content/{id}/child/attachment.
That "create or update" endpoint exists in the Confluence Cloud v1 API
only; Data Center/Server answers it with 405 Method Not Allowed, so
--replace never worked there.
On Data Center/Server, --replace now looks the attachment up by its
exact filename (GET .../child/attachment?filename=) and updates it with
POST .../child/attachment/{attachmentId}/data. When the page has no
attachment with that name, the file is uploaded with the regular create
POST. The comment and minorEdit fields are sent as before.
- Cloud (*.atlassian.net, forceCloud, scoped tokens) keeps the existing
PUT, so its behavior and request count are unchanged.
- Only an exact title match selects the target, so a server that ignores
the filename filter cannot steer the upload at another attachment.
- The data endpoint may answer with a single attachment object instead
of { results: [...] }; both shapes are normalized into `results`.
- README and the bundled SKILL.md state the --replace semantics.
Observed on Confluence Data Center 9.2.9 (PAT bearer auth): PUT on the
collection returns 405, while POST .../{attachmentId}/data returns 200,
increments the attachment version and replaces the content. The tests
mock these responses; CI has no live server.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
pchuri
left a comment
There was a problem hiding this comment.
Thanks for the detailed report, implementation, and Data Center reproduction. The split between the Cloud PUT path and the Data Center/Server lookup-and-POST path looks appropriate, and the exact filename check and response normalization are well covered.
I reviewed the current head and ran the client test suite (269/269 passing) and lint successfully. I did not find a blocking issue in the reviewed changes. I have not independently repeated the live Confluence checks or run the full repository test suite yet, but this looks ready to move toward merge after that final validation. Thanks for the contribution!
## [2.25.9](v2.25.8...v2.25.9) (2026-10-01) ### Bug Fixes * **attachments:** make --replace work on Data Center/Server ([#257](#257)) ([e9163e4](e9163e4)) * **client:** back off when Retry-After is zero or not in the future ([#258](#258)) ([5d9b3b9](5d9b3b9)) * **deps:** update axios to 1.20.0 to unblock security audit ([#263](#263)) ([1f62546](1f62546))
|
🎉 This PR is included in version 2.25.9 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Problem
confluence attachment-upload <pageId> --file a.html --replacefails on Confluence Data Center/Server withRequest failed with status code 405.--replacesendsPUT /rest/api/content/{pageId}/child/attachment. That "create or update attachment" endpoint exists in the Confluence Cloud v1 API, but the Data Center REST API has no PUT on the attachment collection: it offersPOST .../child/attachment(create only) andPOST .../child/attachment/{attachmentId}/data(replace the data of an existing attachment). On Data Center, a filename that already exists can therefore be neither uploaded again (400) nor replaced (405).Observed on Confluence Data Center 9.2.9 (PAT bearer auth,
apiPath/rest/api):attachment-upload <pageId> --file a.html--replaceCannot add a new attachment with same file name as an existing attachment: a.html--replaceRequest failed with status code 405POST /rest/api/content/{pageId}/child/attachment/{attachmentId}/data(multipartfile, headerX-Atlassian-Token: nocheck, optionalminorEdit/comment)Fix
On Data Center/Server (
!isCloud()),--replacenow looks the attachment up by exact filename (GET .../child/attachment?filename=<name>) and POSTs the file to.../child/attachment/{attachmentId}/data. When the page has no attachment with that name, it does the regular create POST.commentandminorEditare sent as before.*.atlassian.net,forceCloud, scoped tokens) keeps the existingPUT, which is an atomic create-or-update, so its behavior and request count are unchanged.filenamefilter cannot make the CLI overwrite another attachment.POST .../{attachmentId}/dataanswers with the updated attachment itself (a single object withtype: "attachment") on Data Center 9.2.9, as the Cloud reference documents, while older Data Center references show{ results: [...] }.uploadAttachmentaccepts both, soresults(and theID/Versionoutput of the command) stays uniform.SKILL.mdnow state the--replacesemantics.Verification
axios-mock-adapterapproach: Data Center replace throughPOST .../{id}/data(headers, multipart body,comment/minorEdit), create when nothing matches, exact-title selection, special characters in the filename, ID encoding, single-object and{ results }responses, failed lookup, no lookup without--replace, and Cloud unchanged for*.atlassian.net,forceCloudand scoped tokens. 9 of them fail onmainwith the 405 above; the other 4 guard the unchanged paths.npm test: 1466 passed (1453 before).npm run lint: clean.--replacewith 405; this branch replaced the attachment twice under the same attachment ID (version 1 → 2 → 3,--commentincluded), the downloaded file matched the last upload, and--replacewith a new filename created a new attachment.Type of Change
Testing
Checklist
🤖 Generated with Claude Code