Filename issue when using Jira Add Attachment REST API

Geoffrey Hughes September 5, 2022

We're using the following API to add attachments to Jira.

curl -D- -u {username}:{password} -X POST -H "X-Atlassian-Token: nocheck" -F "file=@{path/to/file}" http://{base-url}/rest/api/2/issue/{issue-key}/attachments

Unfortunately, if the filename contains square brackets, we get the following error.

<html>

<head>

<title>JIRA &mdash; Internal Server Error</title>

<meta name="decorator" content="none" />

</head>

<body>

<h1>JIRA &mdash; Internal Server Error</h1>

<p>Error reference: fc73345c-4f8a-4891-b866-bf7b09df7a54</p>

<p>Unable to render full error details at this time.

Please consult the error logs for more information.</p>

</body>

</html>

The error log has the following entry.

2022-09-06 13:13:27,095+1200 ajp-nio-127.0.0.1-8009-exec-33 url: /jira/rest/api/2/issue/ISSDDEV-306/attachments; user: dynamics-test WARN dynamics-test 793x286932x1 15xmvji 10.68.130.47 /rest/api/2/issue/ISSDDEV-306/attachments [c.a.j.issue.managers.DefaultAttachmentManager] Cannot create attachment without a filename - inline content? See http://jira.atlassian.com/browse/JRA-10825 (file=attachment-2654463909712498015.tmp).
2022-09-06 13:13:27,096+1200 ajp-nio-127.0.0.1-8009-exec-33 url: /jira/rest/api/2/issue/ISSDDEV-306/attachments; user: dynamics-test ERROR dynamics-test 793x286932x1 15xmvji 10.68.130.47 /rest/api/2/issue/ISSDDEV-306/attachments [c.a.j.r.v2.issue.IssueAttachmentsResource] Error saving attachment
java.lang.NullPointerException
        at com.atlassian.jira.issue.util.DefaultIssueUpdater.storeModifiedFields(DefaultIssueUpdater.java:104)
        at com.atlassian.jira.issue.util.DefaultIssueUpdater.doUpdate(DefaultIssueUpdater.java:94)
        at com.atlassian.jira.issue.util.DefaultIssueUpdater.doUpdate(DefaultIssueUpdater.java:63)
        at jdk.internal.reflect.GeneratedMethodAccessor3985.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
        at com.sun.proxy.$Proxy548.doUpdate(Unknown Source)
        at jdk.internal.reflect.GeneratedMethodAccessor3985.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
        at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
        at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
        at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
        at com.sun.proxy.$Proxy3303.doUpdate(Unknown Source)
        at com.atlassian.jira.rest.v2.issue.IssueAttachmentsResource.addAttachment(IssueAttachmentsResource.java:128)
        at jdk.internal.reflect.GeneratedMethodAccessor3995.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        ... 5 filtered
        at com.atlassian.jira.rest.exception.ExceptionInterceptor.intercept(ExceptionInterceptor.java:55)
        ... 1 filtered
        at com.atlassian.jira.rest.v2.issue.scope.RequestScopeInterceptor.intercept(RequestScopeInterceptor.java:39)
        ... 14 filtered
        at com.atlassian.plugins.rest.module.RestDelegatingServletFilter$JerseyOsgiServletContainer.doFilter(RestDelegatingServletFilter.java:160)
        ... 1 filtered
        at com.atlassian.plugins.rest.module.RestDelegatingServletFilter.doFilter(RestDelegatingServletFilter.java:70)
        ... 32 filtered
        at com.atlassian.servicedesk.internal.web.ExternalCustomerLockoutFilter.doFilter(ExternalCustomerLockoutFilter.java:55)
        ... 8 filtered
        at com.atlassian.jira.plugin.mobile.web.filter.MobileAppRequestFilter.doFilter(MobileAppRequestFilter.java:59)
        ... 4 filtered
        at com.atlassian.jira.plugin.mobile.login.MobileLoginSuccessFilter.doFilter(MobileLoginSuccessFilter.java:54)
        ... 3 filtered
        at com.atlassian.diagnostics.internal.platform.monitor.http.HttpRequestMonitoringFilter.doFilter(HttpRequestMonitoringFilter.java:55)
        ... 8 filtered
        at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
        ... 43 filtered
        at com.atlassian.ratelimiting.internal.filter.RateLimitFilter.doFilter(RateLimitFilter.java:73)
        ... 3 filtered
        at com.atlassian.troubleshooting.thready.filter.AbstractThreadNamingFilter.doFilter(AbstractThreadNamingFilter.java:46)
        ... 17 filtered
        at com.atlassian.jira.security.JiraSecurityFilter.lambda$doFilter$0(JiraSecurityFilter.java:66)
        ... 1 filtered
        at com.atlassian.jira.security.JiraSecurityFilter.doFilter(JiraSecurityFilter.java:64)
        ... 16 filtered
        at com.atlassian.plugins.rest.module.servlet.RestSeraphFilter.doFilter(RestSeraphFilter.java:38)
        ... 3 filtered
        at com.atlassian.pats.web.filter.TokenBasedAuthenticationFilter.doFilter(TokenBasedAuthenticationFilter.java:83)
        ... 19 filtered
        at com.atlassian.jira.servermetrics.CorrelationIdPopulatorFilter.doFilter(CorrelationIdPopulatorFilter.java:30)
        ... 5 filtered
        at com.atlassian.plugins.authentication.impl.basicauth.filter.DisableBasicAuthFilter.doFilter(DisableBasicAuthFilter.java:70)
        ... 3 filtered
        at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.lambda$invokeFilterChain$0(CustomerContextSettingFilter.java:215)
        at com.atlassian.servicedesk.internal.api.util.context.ReentrantThreadLocalBasedCodeContext.rteInvoke(ReentrantThreadLocalBasedCodeContext.java:136)
        at com.atlassian.servicedesk.internal.api.util.context.ReentrantThreadLocalBasedCodeContext.runOutOfContext(ReentrantThreadLocalBasedCodeContext.java:89)
        at com.atlassian.servicedesk.internal.utils.context.CustomerContextServiceImpl.runOutOfCustomerContext(CustomerContextServiceImpl.java:47)
        at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.outOfCustomerContext(CustomerContextSettingFilter.java:206)
        at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.doFilterImpl(CustomerContextSettingFilter.java:134)
        at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.doFilter(CustomerContextSettingFilter.java:123)
        ... 4 filtered
        at com.atlassian.jwt.internal.servlet.JwtAuthFilter.doFilter(JwtAuthFilter.java:37)
        ... 8 filtered
        at com.atlassian.ratelimiting.internal.filter.RateLimitPreAuthFilter.doFilter(RateLimitPreAuthFilter.java:71)
        ... 3 filtered
        at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
        ... 4 filtered
        at com.atlassian.troubleshooting.thready.filter.AbstractThreadNamingFilter.doFilter(AbstractThreadNamingFilter.java:46)
        ... 3 filtered
        at com.atlassian.web.servlet.plugin.LocationCleanerFilter.doFilter(LocationCleanerFilter.java:36)
        ... 26 filtered
        at com.atlassian.jira.servermetrics.MetricsCollectorFilter.doFilter(MetricsCollectorFilter.java:25)
        ... 25 filtered
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.base/java.lang.Thread.run(Unknown Source)
2022-09-06 13:13:27,141+1200 ajp-nio-127.0.0.1-8009-exec-18 url: /jira/rest/api/2/issue/ISSDDEV-306/attachments; user: dynamics-test WARN dynamics-test 793x286933x1 dnd57a 10.68.130.47 /rest/api/2/issue/ISSDDEV-306/attachments [c.a.j.issue.managers.DefaultAttachmentManager] Cannot create attachment without a filename - inline content? See http://jira.atlassian.com/browse/JRA-10825 (file=attachment-14328697622161150902.tmp).
2022-09-06 13:13:27,142+1200 ajp-nio-127.0.0.1-8009-exec-18 url: /jira/rest/api/2/issue/ISSDDEV-306/attachments; user: dynamics-test ERROR dynamics-test 793x286933x1 dnd57a 10.68.130.47 /rest/api/2/issue/ISSDDEV-306/attachments [c.a.j.r.v2.issue.IssueAttachmentsResource] Error saving attachment
java.lang.NullPointerException
        at com.atlassian.jira.issue.util.DefaultIssueUpdater.storeModifiedFields(DefaultIssueUpdater.java:104)
        at com.atlassian.jira.issue.util.DefaultIssueUpdater.doUpdate(DefaultIssueUpdater.java:94)
        at com.atlassian.jira.issue.util.DefaultIssueUpdater.doUpdate(DefaultIssueUpdater.java:63)
        at jdk.internal.reflect.GeneratedMethodAccessor3985.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
        at com.sun.proxy.$Proxy548.doUpdate(Unknown Source)
        at jdk.internal.reflect.GeneratedMethodAccessor3985.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
        at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
        at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
        at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136)
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
        at com.sun.proxy.$Proxy3303.doUpdate(Unknown Source)
        at com.atlassian.jira.rest.v2.issue.IssueAttachmentsResource.addAttachment(IssueAttachmentsResource.java:128)
        at jdk.internal.reflect.GeneratedMethodAccessor3995.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        ... 5 filtered
        at com.atlassian.jira.rest.exception.ExceptionInterceptor.intercept(ExceptionInterceptor.java:55)
        ... 1 filtered
        at com.atlassian.jira.rest.v2.issue.scope.RequestScopeInterceptor.intercept(RequestScopeInterceptor.java:39)
        ... 14 filtered
        at com.atlassian.plugins.rest.module.RestDelegatingServletFilter$JerseyOsgiServletContainer.doFilter(RestDelegatingServletFilter.java:160)
        ... 1 filtered
        at com.atlassian.plugins.rest.module.RestDelegatingServletFilter.doFilter(RestDelegatingServletFilter.java:70)
        ... 32 filtered
        at com.atlassian.servicedesk.internal.web.ExternalCustomerLockoutFilter.doFilter(ExternalCustomerLockoutFilter.java:55)
        ... 8 filtered
        at com.atlassian.jira.plugin.mobile.web.filter.MobileAppRequestFilter.doFilter(MobileAppRequestFilter.java:59)
        ... 4 filtered
        at com.atlassian.jira.plugin.mobile.login.MobileLoginSuccessFilter.doFilter(MobileLoginSuccessFilter.java:54)
        ... 3 filtered
        at com.atlassian.diagnostics.internal.platform.monitor.http.HttpRequestMonitoringFilter.doFilter(HttpRequestMonitoringFilter.java:55)
        ... 8 filtered
        at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
        ... 43 filtered
        at com.atlassian.ratelimiting.internal.filter.RateLimitFilter.doFilter(RateLimitFilter.java:73)
        ... 3 filtered
        at com.atlassian.troubleshooting.thready.filter.AbstractThreadNamingFilter.doFilter(AbstractThreadNamingFilter.java:46)
        ... 17 filtered
        at com.atlassian.jira.security.JiraSecurityFilter.lambda$doFilter$0(JiraSecurityFilter.java:66)
        ... 1 filtered
        at com.atlassian.jira.security.JiraSecurityFilter.doFilter(JiraSecurityFilter.java:64)
        ... 16 filtered
        at com.atlassian.plugins.rest.module.servlet.RestSeraphFilter.doFilter(RestSeraphFilter.java:38)
        ... 3 filtered
        at com.atlassian.pats.web.filter.TokenBasedAuthenticationFilter.doFilter(TokenBasedAuthenticationFilter.java:83)
        ... 19 filtered
        at com.atlassian.jira.servermetrics.CorrelationIdPopulatorFilter.doFilter(CorrelationIdPopulatorFilter.java:30)
        ... 5 filtered
        at com.atlassian.plugins.authentication.impl.basicauth.filter.DisableBasicAuthFilter.doFilter(DisableBasicAuthFilter.java:70)
        ... 3 filtered
        at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.lambda$invokeFilterChain$0(CustomerContextSettingFilter.java:215)
        at com.atlassian.servicedesk.internal.api.util.context.ReentrantThreadLocalBasedCodeContext.rteInvoke(ReentrantThreadLocalBasedCodeContext.java:136)
        at com.atlassian.servicedesk.internal.api.util.context.ReentrantThreadLocalBasedCodeContext.runOutOfContext(ReentrantThreadLocalBasedCodeContext.java:89)
        at com.atlassian.servicedesk.internal.utils.context.CustomerContextServiceImpl.runOutOfCustomerContext(CustomerContextServiceImpl.java:47)
        at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.outOfCustomerContext(CustomerContextSettingFilter.java:206)
        at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.doFilterImpl(CustomerContextSettingFilter.java:134)
        at com.atlassian.servicedesk.internal.web.CustomerContextSettingFilter.doFilter(CustomerContextSettingFilter.java:123)
        ... 4 filtered
        at com.atlassian.jwt.internal.servlet.JwtAuthFilter.doFilter(JwtAuthFilter.java:37)
        ... 8 filtered
        at com.atlassian.ratelimiting.internal.filter.RateLimitPreAuthFilter.doFilter(RateLimitPreAuthFilter.java:71)
        ... 3 filtered
        at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
        ... 4 filtered
        at com.atlassian.troubleshooting.thready.filter.AbstractThreadNamingFilter.doFilter(AbstractThreadNamingFilter.java:46)
        ... 3 filtered
        at com.atlassian.web.servlet.plugin.LocationCleanerFilter.doFilter(LocationCleanerFilter.java:36)
        ... 26 filtered
        at com.atlassian.jira.servermetrics.MetricsCollectorFilter.doFilter(MetricsCollectorFilter.java:25)
        ... 25 filtered
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.base/java.lang.Thread.run(Unknown Source)
2022-09-06 13:13:27,388+1200 ajp-nio-127.0.0.1-8009-exec-32 url: /jira/rest/api/2/issue/ISSDDEV-306/attachments; user: dynamics-test INFO dynamics-test 793x286934x1 orq8l7 10.68.130.47 /rest/api/2/issue/ISSDDEV-306/attachments [c.a.jira.index.QueueingIndexStats] [JIRA-STATS] [INDEXING-QUEUE]  index:ISSUE, total primary queue stats: {"maxQueueSize":1,"putCounter":141,"queueFullOnPut":0,"getCounter":140,"timeInQueueMillis":{"count":140,"min":0,"max":6,"sum":39,"avg":0,"distributionCounter":{"0":114,"1":21,"10":5,"100":0,"1000":0,"10000":0,"20000":0,"30000":0}},"timeToUpdateIndexMillis":{"count":140,"min":0,"max":25,"sum":116,"avg":0,"distributionCounter":{"0":98,"1":32,"10":7,"50":3,"100":0,"500":0,"1000":0}},"totalTimeMillis":{"count":140,"min":1,"max":28,"sum":476,"avg":3,"distributionCounter":{"0":0,"1":12,"10":120,"100":8,"1000":0,"10000":0,"20000":0,"30000":0}},"totalTimeFailedMillis":{"count":0,"min":0,"max":0,"sum":0,"avg":0,"distributionCounter":{}},"totalTimeTimedOutMillis":{"count":0,"min":0,"max":0,"sum":0,"avg":0,"distributionCounter":{}},"putCounterByThreadTopN":{"ajp-nio-127.0.0.1-8009-exec-15 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":8,"ajp-nio-127.0.0.1-8009-exec-27 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":8,"ajp-nio-127.0.0.1-8009-exec-17 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":6,"ajp-nio-127.0.0.1-8009-exec-18 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":6,"ajp-nio-127.0.0.1-8009-exec-2 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":6,"ajp-nio-127.0.0.1-8009-exec-21 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":6,"ajp-nio-127.0.0.1-8009-exec-31 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":6,"ajp-nio-127.0.0.1-8009-exec-13 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":4,"ajp-nio-127.0.0.1-8009-exec-20 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":4,"ajp-nio-127.0.0.1-8009-exec-26 url: /jira/rest/jpo/1.0/issues/commit; user: ransa46p":4},"indexerNotRunningCounter":0}
2022-09-06 13:13:27,388+1200 ajp-nio-127.0.0.1-8009-exec-32 url: /jira/rest/api/2/issue/ISSDDEV-306/attachments; user: dynamics-test INFO dynamics-test 793x286934x1 orq8l7 10.68.130.47 /rest/api/2/issue/ISSDDEV-306/attachments [c.a.jira.index.QueueingIndexStats] [JIRA-STATS] [INDEXING-QUEUE]  index:ISSUE, total secondary queue stats: {"maxQueueSize":2,"putCounter":10,"queueFullOnPut":0,"getCounter":10,"timeInQueueMillis":{"count":10,"min":0,"max":66,"sum":86,"avg":8,"distributionCounter":{"0":6,"1":0,"10":3,"100":1,"1000":0,"10000":0,"20000":0,"30000":0}},"timeToUpdateIndexMillis":{"count":10,"min":0,"max":66,"sum":104,"avg":10,"distributionCounter":{"0":3,"1":0,"10":5,"50":1,"100":1,"500":0,"1000":0}},"totalTimeMillis":{"count":10,"min":0,"max":69,"sum":196,"avg":19,"distributionCounter":{"0":1,"1":0,"10":5,"100":4,"1000":0,"10000":0,"20000":0,"30000":0}},"totalTimeFailedMillis":{"count":0,"min":0,"max":0,"sum":0,"avg":0,"distributionCounter":{}},"totalTimeTimedOutMillis":{"count":0,"min":0,"max":0,"sum":0,"avg":0,"distributionCounter":{}},"putCounterByThreadTopN":{"RMI TCP Connection(29)-10.68.1.47":3,"Caesium-1-4":2,"RMI TCP Connection(37)-10.68.1.47":2,"RMI TCP Connection(34)-10.68.1.47":1,"SdOffThreadEventJobRunner:thread-5":1,"devstatus.applink:thread-3":1},"indexerNotRunningCounter":0}
 

If we remove the square brackets from the filename, it is added to Jira successfully.

 

1 comment

David Bakkers
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 6, 2022

Hello @Geoffrey Hughes 

Since Jira has no problems attaching files with square brackets in their name, then it can be logically deducted that the fault is in your cURL request.

Google 'cURL sanitise filenames' or 'cURL encode filenames' for the answer.

Geoffrey Hughes September 6, 2022

I just used curl as an example.  It's happening in Postman and with our integration platform.

Encoding the square brackets in my request does put the file into Jira but it keeps the encoded values when viewing in Jira.

Looking at how it's handled in a web browser, it's using a combination of 2 APIs. 

  • /rest/internal/2/AttachTemporaryFile
  • /rest/jddap/1.0/attachment
David Bakkers
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 6, 2022

I just noticed you are still using Jira SERVER, not Jira CLOUD.

Uploading files with UTF-8 chars in their name is no problem with Jira Cloud. I just tested with Postman and a file named 'Test [file].txt' and it worked just fine:

Untitled.png

There are numerous articles regarding Jira Server and UTF-8 encoding issues with attachments, such as JRASERVER-32824. I think you should review the bugs list and see if a specific one affects your particular version of Jira Server and what you need to do to compensate for it.

 

Geoffrey Hughes September 6, 2022

I want to be able to submit a file with square brackets to Jira via the /rest/api/2/issue/{issue-key}/attachments rest API.

When I try in Postman, I get the internal server error I originally posted.

What version of Jira did you try against?  We're not using Jira cloud.

Can you send the output of the raw log from the Postman console?

David Bakkers
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 6, 2022

As I said, I tested against Jira Cloud. I don't have access to a Jira Server instance at the moment and I no longer maintain my own instance.

For Jira Cloud I tested against both the v2 and v3 issue attachment endpoints and got the exact same result.

Here is the console log from Postman for a test against the v2 endpoint:

POST /rest/api/2/issue/BPT-232/attachments HTTP/1.1
X-Atlassian-Token: no-check
Authorization: Basic <--REDACTED-->
User-Agent: PostmanRuntime/7.29.2
Accept: */*
Cache-Control: no-cache
Postman-Token: 77ed735b-8694-4fe6-8eb2-42451a8eb982
Host: solidinterface.atlassian.net
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------524991648008426292662601
Cookie: atlassian.xsrf.token= <--REDACTED-->
Content-Length: 722

----------------------------524991648008426292662601
Content-Disposition: form-data; name="file"; filename="test [file].txt"
<test [file].txt>
----------------------------524991648008426292662601--

HTTP/1.1 200 OK
Date: Tue, 06 Sep 2022 23:23:28 GMT
Content-Type: application/json;charset=UTF-8
Server: globaledge-envoy
Timing-Allow-Origin: *
X-Arequestid: 783a9958-a97f-4e24-bc2f-88dbe817896f
X-Aaccountid: 5efea30ea115ac0bb540e9cb
Cache-Control: no-cache, no-store, no-transform
Vary: Accept-Encoding
Content-Encoding: br
X-Envoy-Upstream-Service-Time: 780
Expect-Ct: report-uri="https://web-security-reports.services.atlassian.com/expect-ct-report/atlassian-proxy", max-age=86400
Strict-Transport-Security: max-age=63072000; preload
X-Content-Type-Options: nosniff
X-Xss-Protection: 1; mode=block
Atl-Traceid: 534bf156eb348c83
Report-To: {"endpoints": [{"url": "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1", "include_subdomains": true, "max_age": 600}
Nel: {"failure_fraction": 0.001, "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}
Transfer-Encoding: chunked

[{"self":"https://solidinterface.atlassian.net/rest/api/2/attachment/10017","id":"10017","filename":"test [file].txt","author":{"self":"https://solidinterface.atlassian.net/rest/api/2/user?accountId=5efea30ea115ac0bb540e9cb","accountId":"5efea30ea115ac0bb540e9cb","emailAddress":"asolidinterface@gmail.com","avatarUrls":{"48x48":"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/5efea30ea115ac0bb540e9cb/c9e1b693-81bf-4eef-9848-3b844f31f01c/48","24x24":"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/5efea30ea115ac0bb540e9cb/c9e1b693-81bf-4eef-9848-3b844f31f01c/24","16x16":"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/5efea30ea115ac0bb540e9cb/c9e1b693-81bf-4eef-9848-3b844f31f01c/16","32x32":"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/5efea30ea115ac0bb540e9cb/c9e1b693-81bf-4eef-9848-3b844f31f01c/32"},"displayName":"solid interface","active":true,"timeZone":"Australia/Sydney","accountType":"atlassian"},"created":"2022-09-07T09:23:28.203+1000","size":509,"mimeType":"text/plain","content":"https://solidinterface.atlassian.net/rest/api/2/attachment/content/10017"}]

 

Have fun.

PS. You are currently posting in the Discussion section of a public forum. If you have genuine technical problems with a specific API call to a specific version of Jira Server, you should be posting this as a Question in the Atlassian Developer's community.

Geoffrey Hughes September 6, 2022

Thanks.  I found my issue.  

Whomever setup the Postman collection added another Content-Type header without the boundary part of it.

Removing the second duplicate header and the Postman request has worked.

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events