[Bug]: Assignee (AssignedTo, ResolvedBy, ClosedBy) is set to migration user
Version
- [x] I confirm that I am using the latest version
Source Version
Azure DevOps Server 2022
Target Version
Azure DevOps Service
Relevant configuration
// We disabled that because the usernames have chars like ö in it.
"StringManipulatorTool": {
"Enabled": false
},
"TfsUserMappingTool": {
"Enabled": true,
"UserMappingFile": "D:\\Temp\\userExport.json",
"MatchUsersByEmail": true,
"IdentityFieldsToCheck": [
"System.AssignedTo",
"System.ChangedBy",
"System.CreatedBy",
"Microsoft.VSTS.Common.ActivatedBy",
"Microsoft.VSTS.Common.ResolvedBy",
"Microsoft.VSTS.Common.ClosedBy"
]
}
"Processors": [
{
"ProcessorType": "TfsWorkItemMigrationProcessor",
"Enabled": true,
"UpdateCreatedDate": true,
"UpdateCreatedBy": true,
//"WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.Id] > 9000 and [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc",
"WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject and [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc",
"FixHtmlAttachmentLinks": true,
"WorkItemCreateRetryLimit": 5,
"FilterWorkItemsThatAlreadyExistInTarget": false,
"PauseAfterEachWorkItem": false,
"AttachRevisionHistory": false,
"GenerateMigrationComment": false,
"SourceName": "Source",
"TargetName": "Target",
"WorkItemIDs": [],
"MaxGracefulFailures": 4,
"SkipRevisionWithInvalidIterationPath": false,
"SkipRevisionWithInvalidAreaPath": false
},
Relevant log output
Variant 1:
[11:39:09 DBG] [16.2.9] ######################################################################################
[11:39:09 DBG] [16.2.9] ProcessWorkItem: 9005
[11:39:09 DBG] [16.2.9] ######################################################################################
[11:39:09 DBG] [16.2.9] WorkItemQuery: ===========GetWorkItems=============
..
[11:39:09 DBG] [16.2.9] Query sent
[11:39:09 INF] [16.2.9] [ Task][Complete: 472/8132][sid: 9005|Rev: 8][tid: null | Work Item has 8 revisions and revision migration is set to true
[11:39:09 DBG] [16.2.9] TfsRevisionManagerTool::EnforceDatesMustBeIncreasing
[11:39:09 INF] [16.2.9] Source: Found 8 revisions to migrate on Work item:Source
[11:39:09 DBG] [16.2.9] Source: RevisionsToMigrate:----------------------------------------------------
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:0 - Number:1 - ChangedDate:"2025-06-04T14:03:12.3400000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:1 - Number:2 - ChangedDate:"2025-06-04T14:03:37.0700000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:2 - Number:3 - ChangedDate:"2025-06-05T08:49:17.7870000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:3 - Number:4 - ChangedDate:"2025-06-05T08:49:20.7470000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:4 - Number:5 - ChangedDate:"2025-06-05T09:00:04.3570000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:5 - Number:6 - ChangedDate:"2025-06-05T09:05:43.0730000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:6 - Number:7 - ChangedDate:"2025-06-10T17:03:47.3100000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:7 - Number:8 - ChangedDate:"2025-06-13T17:17:26.6230000+02:00"
[11:39:09 DBG] [16.2.9] Source: RevisionsToMigrate:----------------------------------------------------
[11:39:09 DBG] [16.2.9] Target: RevisionsToMigrate: No revisions to migrate
[11:39:09 DBG] [16.2.9] TfsRevisionManagerTool::GetRevisionsToMigrate: Raw Source Has 8 revisions
[11:39:09 DBG] [16.2.9] TfsRevisionManagerTool::GetRevisionsToMigrate::RemoveRevisionsAlreadyOnTarget Target is null
[11:39:09 INF] [16.2.9] Source: Found 8 revisions to migrate on Work item:Source
[11:39:09 DBG] [16.2.9] Source: RevisionsToMigrate:----------------------------------------------------
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:0 - Number:1 - ChangedDate:"2025-06-04T14:03:12.3400000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:1 - Number:2 - ChangedDate:"2025-06-04T14:03:37.0700000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:2 - Number:3 - ChangedDate:"2025-06-05T08:49:17.7870000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:3 - Number:4 - ChangedDate:"2025-06-05T08:49:20.7470000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:4 - Number:5 - ChangedDate:"2025-06-05T09:00:04.3570000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:5 - Number:6 - ChangedDate:"2025-06-05T09:05:43.0730000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:6 - Number:7 - ChangedDate:"2025-06-10T17:03:47.3100000+02:00"
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:7 - Number:8 - ChangedDate:"2025-06-13T17:17:26.6230000+02:00"
[11:39:09 DBG] [16.2.9] Source: RevisionsToMigrate:----------------------------------------------------
..
[11:39:09 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|Microsoft.VSTS.Common.ClosedBy]
[11:39:09 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|Microsoft.VSTS.Common.ActivatedBy]
[11:39:09 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|System.CreatedBy]
[11:39:09 DBG] [16.2.9] PopulateWorkItem:FieldUpdate: System.CreatedBy | Source:ORIGINALUSER Target:CLOUDUSER
[11:39:09 DBG] [16.2.9] StringManipulatorTool::ProcessString
[11:39:09 DBG] [16.2.9] StringManipulatorTool::ProcessString::Disabled
[11:39:09 DBG] [16.2.9] PopulateWorkItem:FieldUpdate: System.CreatedDate | Source:"2025-06-04T14:03:12.3400000+02:00" Target:"2025-06-04T14:03:12.3400000+02:00"
[11:39:09 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|System.AssignedTo]
[11:39:09 DBG] [16.2.9] PopulateWorkItem:FieldUpdate: System.AssignedTo | Source: Target:MIGRATIONUSER
[11:39:09 DBG] [16.2.9] TfsExtensions::SaveToAzureDevOps
[11:39:09 DBG] [16.2.9] TfsExtensions::SaveToAzureDevOps: ChangedBy: ORGINALUSER, AuthorisedBy: MIGRATIONUSER
[11:39:10 INF] [16.2.9] [ Task][Complete: 472/8132][sid: 9005|Rev: 8][tid: null | Saved TargetWorkItem 551852. Replayed revision 1 of 8
Variant 2:
[11:39:21 INF] [16.2.9] [ Task][Complete: 472/8132][sid: 9005|Rev: 8][tid: null | Processing Revision [7]
[11:39:21 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|Microsoft.VSTS.Common.ClosedBy]
[11:39:21 DBG] [16.2.9] PopulateWorkItem:FieldUpdate: Microsoft.VSTS.Common.ClosedBy | Source:ORGINALUSER Target:MIGRATIONUSER
What happened?
The migration is working successfully, but assignee are changed
- Work item (bug, task) is created Bug-Variant 1: "Assigned To" is empy in the source, but is set to migration user
- Work item is closed: ClosedBy is set to correct user
- Work item is updated (attachment or link added) Bug-Variant 2: ClosedBy is changed from the correct user to migration user
Debug in Visual Studio
- [x] Visual Studio Debug
This usually only happens when the user running the migration does not have "on behalf of" permission...
@MrHinsh but it works fine to set it: Rev 1: Work item is created and Assignee is set to MIGRATIONUSER. History entry: "ORIGINALCREATOR (via MIGRATIONUSER) created the XY I suspect that an empty assignee is set to the default -> creator (Bug Variant 1) Rev 2: Work item is assigned to a normal person (change of state from Proposed to active) Rev 3: ORIGINALUSER changes state from active to closed -> Resolved by is set to MIGRATIONUSER and Closed By to ORGINALUSER Rev 4: "Migration (via MIGRATIONUSER)" -> Closed By is changed from ORGINALUSER to MIGRATIONUSER and Parent link is added.
So, I assume these are multiple bugs, but I fear "on behalf of" is not the problem.
Still happens with the latest version 16.3.1
Hi @JDAVF and @MrHinsh,
Thanks for the detailed reproduction steps and logs. From what I understand, the behavior seems to be caused by how TfsUserMappingTool interacts with TfsWorkItemMigrationProcessor when an empty assignee or state changes occur. Specifically:
- Bug Variant 1: An empty "Assigned To" gets set to MIGRATIONUSER by default.
- Bug Variant 2: The "Closed By" field changes from the correct user to MIGRATIONUSER during state transitions.
It looks like multiple related issues are occurring rather than a single cause, and the "on behalf of" permission explanation may not fully address it.
I’d be happy to help investigate this further. My plan would be to:
- Examine the user mapping logic in TfsUserMappingTool and its integration with work item revisions.
- Identify why certain revisions or state changes override the mapped user fields.
- Propose a fix or at least suggest configuration adjustments to preserve the original user assignments.
Could I get assigned to this issue so I can start exploring it? I’ll provide updates as I make progress.