Since February 3rd Forge issue panels are receiving incorrect value of localId
inside of productContext
. The value is equal to extensionId
, while it should be in the format of {extensionId}-{panelInstanceId}-{timestamp}
.
The app is affected if it uses localId
to store panel data based on it. As we are dealing with data write under different key there will most likely be required data migration to recover from this scenario. Without action from app side it probably will be impossible to recover without loosing some data. If the app could be made ready for revert of incorrect localId
behavior then we can carefully execute this change without loosing any data.
We initially released change to all production with exception to affected apps on affected tenants - the intention here is to prevent situation where app will suddenly lose connection to it’s data. If an app is affected and an update is required, the app owner received a notification from us in the form of an ADDON ticket on the ecosystem.atlassian.net site to coordinate the migration carefully. You can find the ADDON tickets assigned to your user by using this JQL.
If you haven’t been contacted and believe that any of your apps is affected, please reach out to us by creating an ECOHELP ticket (submit a request here: Report a bug ) with summary “Forge Issue Panels - localId”.
Here is potential solution on app side: when app receives the localId
in a form of only {extensionId}
it should keep working as is - load and store from {extensionId}|${issueKey}
- we assume it’s current behavior. When app received the correct localId
which is not equal to {extensionId}
the app could ask for data from both keys and decide on result - pick the data to use and migrate data if it’s required.
Below there is a snippet with high-level potential fix. The logic needs to be kept “forever” or you can additionally perform background data migration before mitigation code is removed.
if (localId == extensionId) {
// this is the current situation
data = storage.get(`${localId}|${issueKey}`)
// use the data
} else {
// code path for the case where the localId != extensionId - which will happen once we apply the fix
data = storage.get(`${localId}|${issueKey}`)
dataToMigrate = storage.get(`${extensionId}|${issueKey}`)
if (data && !dataToMigrate) {
// normal path - use data as usuall
} else if (dataToMigrate && !data) {
// data migration path - recovery from wrong localId
storage.set(`${localId}|${issueKey}`, dataToMigrate)
storage.delete(`${extensionId}|${issueKey}`)
data = dataToMigrate
// use data as usual from now - next execution will go to the normal path
} else if (data && dataToMigrate) {
// collision - resolve conflict (pick newer or show error and ask user to decide which data should be keept)
// should only happen if panel used to have data and then was overridden with new data after localId changed
}
}