Access denied on gateway.getCloudFeedback(transferId)

I note that JCMA version 1.5.7 has been released. I’ve upgraded my Jira dev server to that version.

Here is the Java code that I’m using to fetch feedback:

BoundCloudMigrationGateway gateway = cloudMigrationAccessor.getCloudMigrationGateway();
Map<String, Object> feedback = gateway.getCloudFeedback(transferId);

The call to getCloudFeedback terminates with an exception:

[INFO] [talledLocalContainer] [ERROR] 2021-07-15 13:43:19,877 jersey.ThrowableExceptionMapper - Uncaught exception thrown by REST service: Received an unexpected status code; expected [200], but received 403: Access denied.
[INFO] [talledLocalContainer] com.atlassian.jira.migration.httpclient.exceptions.UnexpectedStatusCodeException: Received an unexpected status code; expected [200], but received 403: Access denied.
[INFO] [talledLocalContainer]   at com.atlassian.jira.migration.httpclient.exceptions.ExceptionsKt.unexpectedStatusCode(Exceptions.kt:7)
[INFO] [talledLocalContainer]   at com.atlassian.jira.migration.httpclient.AbstractPluginHttpClient.checkExpectedResponseCode(AbstractPluginHttpClient.kt:134)
[INFO] [talledLocalContainer]   at com.atlassian.jira.migration.httpclient.AbstractPluginHttpClient.execute(AbstractPluginHttpClient.kt:63)
[INFO] [talledLocalContainer]   at com.atlassian.jira.migration.httpclient.AbstractPluginHttpClient.execute(AbstractPluginHttpClient.kt:51)
[INFO] [talledLocalContainer]   at com.atlassian.jira.migration.amsclient.DefaultAppMigrationServiceClient.getCloudFeedback(DefaultAppMigrationServiceClient.kt:181)

Can you help me understand why I’m getting the 403 ACCESS DENIED error?

I’ve just now restarted everything, and now I’m getting this:

[INFO] [talledLocalContainer] [ERROR] 2021-07-15 16:01:29,657 jersey.ThrowableExceptionMapper - Uncaught exception thrown by REST service: Parameter specified as non-null is null: method com.atlassian.jira.migration.amsclient.Defa
ultAppMigrationServiceClient.getCloudFeedback, parameter cloudId
[INFO] [talledLocalContainer] java.lang.NullPointerException: Parameter specified as non-null is null: method com.atlassian.jira.migration.amsclient.DefaultAppMigrationServiceClient.getCloudFeedback, parameter cloudId
[INFO] [talledLocalContainer]   at com.atlassian.jira.migration.amsclient.DefaultAppMigrationServiceClient.getCloudFeedback(DefaultAppMigrationServiceClient.kt)
[INFO] [talledLocalContainer]   at
[INFO] [talledLocalContainer]   at

I’m baffled.

Hi @david.pinn,

Thanks for your posts and sorry to hear you’re having this issue. To help, I’d like to get some information about your implementation.

  • Are you testing on Data Centre (with one or more than one node) or Server?
  • What timezone are the logs in?

I can see in the logs a lot of testing for /feedback returning 400, 403 and 404 errors.

You’ll get a 404 if no feedback has been sent from cloud before making a request.
You’ll get a 403 if the transferId is not associated with the cloudId attached to the migration. This might happen if there’s a cache flush during migration, or the link has timed out.
The 400 is an odd one, and I need to investigate that more.

I sometimes find, as developer we tend to restart/flush/update/re-install a little to much and the code isn’t resilient enough to handle that. Can I ask you try with a fresh restart of Jira, and a new clean Cloud site?


1 Like

Thanks for your thoughts on this, @jrichards. To answer your questions:

  • I’m testing on a Jira Server instance, using AMPS
  • The logs are in the Sydney timezone (AEST, UTC+10)

I had another go at this today. I spun up a new Jira Cloud instance and ran a new migration into it. Then I tried to fetch the feedback on the server-side. Initially, I saw 403 errors. Then I remembered your comments about cache flushing and thought that perhaps a cleansing re-start of my server instance might help. Having done that, I’m now getting the “Parameter specified as non-null is null” error:

[INFO] [talledLocalContainer] Jul 21, 2021 4:15:03 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
[INFO] [talledLocalContainer] INFO: Initiating Jersey application, version 'Jersey: 1.19.4 05/24/2017 03:20 PM'
[INFO] [talledLocalContainer] java.lang.NullPointerException: Parameter specified as non-null is null: method com.atlassian.jira.migration.amscl
ient.DefaultAppMigrationServiceClient.getCloudFeedback, parameter cloudId
[INFO] [talledLocalContainer]   at com.atlassian.jira.migration.amsclient.DefaultAppMigrationServiceClient.getCloudFeedback(DefaultAppMigrationS
[INFO] [talledLocalContainer]   at
[INFO] [talledLocalContainer]   at

Just a thought, but could it be that I’m not initializing the CloudMigrationAccessor properly or something? I’m clutching at straws because I believe my use of the JCMA Java API to be pretty straightforward. Are you aware of anybody who is successfully using the feedback API?

Hi @david.pinn,

I can see the errors in our logs and the 403 is telling me the transferId you’re using is not related to the cloudId you’re doing the transfer to.

The transferId is the value you get from the first argument of onStartAppMigration(). The cloudId is something we keep internal and don’t expose, so should never be null.

And now I read the start of the chain again, in answer to your question

The answer is yes.

I’d recommend looking at the code we have in our basic-sample in Bitbucket.

Looking at this again

BoundCloudMigrationGateway gateway = cloudMigrationAccessor.getCloudMigrationGateway();
Map<String, Object> feedback = gateway.getCloudFeedback(transferId);

Grabbing the gateway like this isn’t what we usually do. Where did you get this example from?

I’d recommend going back to our Getting Started page and reviewing the documentation for using the -tracker or -osgi library.

Specifically, depending on the library you use, implement the interface and save away the reference to gateway and register the listener in the constructor.

Hope this helps.


The Basic Sample uses atlassian-app-cloud-migration-osgi and has this to say about itself:

“This example is not recommended as a basis for production use and is provided only as a basic example.”

For that reason, I instead followed the Buffer Sample, which describes itself as

“… recommended as a basis for production use”.

Here is the constructor of the CloudMigrationListenerV1 implementation in the Buffer Sample:

public MyPluginComponentImpl(LocalCloudMigrationAccessor accessor) {
    // It is not safe to save a direct reference to the 
    // gateway as that can change over time
    this.accessor = accessor.getCloudMigrationAccessor();

The Java comment in there convinced me to retain a reference to the accessor, not the gateway; was that incorrect?

Hi @david.pinn,

That comment is correct. I’d like to see all your code to understand what’s going on. What time is good for a call?


How about 12pm?

That’s now… what’s your email address?

After discussing this issue with James on a Zoom call, I now understand the cause. It’s simply that I have been calling gateway.getCloudFeedback(transferId) after the transfer has completed. It turns out that getCloudFeedback should only be called before a terminating event, signaled by the Cloud sending a Success, Failed, or Incomplete status update, has occurred.

Under the covers, an association between the transferId and a cloudId is formed during the progress of a migration, and once the transfer has terminated, that association is discarded. If you then call getCloudFeedback you get an error indicating that the cloudId parameter is null.

Note this entry in the Migrations Glossary:

Settled transfer : A transfer for which the status is any of SUCCESS , FAILED or INCOMPLETE . Any request made using the transferId of a settled transfer will return a 4XX response code.”

Thanks for your time in getting my head sorted on this, James.

1 Like