-
Notifications
You must be signed in to change notification settings - Fork 1
fix: timeTrackingWidget not supported on WorkItemCreateInput - GitLab API limitation #193
Description
Summary
When creating a work item with the timeEstimate parameter, the following error occurs:
GraphQL errors: Variable $input of type WorkItemCreateInput! was provided invalid value
for timeTrackingWidget (Field is not defined on WorkItemCreateInput)
Root Cause Analysis
GitLab GraphQL API does NOT support timeTrackingWidget in WorkItemCreateInput.
Evidence
-
MR !143507 - "Add time tracking on create work item":
"This MR adds the capability of setting time estimate and time spent through quick actions when creating a work item through its description."
Time tracking on create is implemented ONLY via quick actions in description (e.g.,
/estimate 1h 30m), not as a direct widget input. -
Epic Work Items API Migration Guide lists supported widgets for creation:
colorWidget✅descriptionWidget✅healthStatusWidget✅startAndDueDateWidget✅assigneesWidget✅
timeTrackingWidgetis NOT mentioned!
Current Code Behavior
// src/entities/workitems/registry.ts:511-512
if (timeEstimate !== undefined) {
createInput.timeTrackingWidget = { timeEstimate }; // ❌ NOT supported by GitLab API
}This works correctly on update (lines 689-695), but fails on create.
Solution: Two-Step Create + Update with Graceful Degradation
Since timeTrackingWidget works on update, use a two-step approach:
- Create work item WITHOUT
timeTrackingWidget - Update immediately with
timeEstimateusing the working update mechanism - If update fails - return successful create result with warning about what wasn't applied
Implementation
// Step 1: Remove timeTrackingWidget from create input
const { timeEstimate, ...createInputWithoutTime } = createInput;
// Step 2: Create work item
const createResponse = await client.request(CREATE_WORK_ITEM_WITH_WIDGETS, {
input: createInputWithoutTime,
});
const createdWorkItem = createResponse.workItemCreate?.workItem;
if (!createdWorkItem) {
throw new Error("Work item creation failed");
}
// Step 3: If timeEstimate was requested, apply it via update
if (timeEstimate !== undefined) {
try {
const updateResponse = await client.request(UPDATE_WORK_ITEM, {
input: {
id: createdWorkItem.id,
timeTrackingWidget: { timeEstimate },
},
});
// Return updated work item with time estimate applied
return cleanWorkItemResponse(updateResponse.workItemUpdate.workItem);
} catch (updateError) {
// Update failed - return create result with warning
return {
...cleanWorkItemResponse(createdWorkItem),
_warning: {
message: "Work item created successfully, but some properties could not be applied",
failedProperties: {
timeEstimate: {
requestedValue: timeEstimate,
error: updateError.message,
},
},
},
};
}
}
return cleanWorkItemResponse(createdWorkItem);Response Format on Partial Success
{
"id": "12345",
"iid": "42",
"title": "My Issue",
"webUrl": "https://gitlab.example.com/group/project/-/issues/42",
"_warning": {
"message": "Work item created successfully, but some properties could not be applied",
"failedProperties": {
"timeEstimate": {
"requestedValue": "2h 30m",
"error": "Time tracking widget update failed: insufficient permissions"
}
}
}
}Additional Widgets to Verify
Need to check which other widgets may have the same issue (not supported on create):
| Widget | Our Code | GitLab API on create | Status |
|---|---|---|---|
assigneesWidget |
✅ | ✅ Supported | OK |
labelsWidget |
✅ | ❓ Needs verification | TEST |
milestoneWidget |
✅ | ❓ Needs verification | TEST |
hierarchyWidget |
✅ | ❓ Needs verification | TEST |
startAndDueDateWidget |
✅ | ✅ Supported | OK |
timeTrackingWidget |
✅ | ❌ NOT supported | BUG |
weightWidget |
✅ | ❓ Needs verification | TEST |
iterationWidget |
✅ | ❓ Needs verification | TEST |
healthStatusWidget |
✅ | ✅ Supported | OK |
progressWidget |
✅ | ❓ Needs verification | TEST |
colorWidget |
✅ | ✅ Supported | OK |
Important: If other widgets also fail on create, apply the same two-step pattern. Accumulate all failed properties in the _warning.failedProperties object.
Testing Requirements
1. Integration Tests - Widget Combinations
describe('manage_work_item create with widgets', () => {
it('should create work item with title only', async () => {});
it('should create work item with startDate + dueDate', async () => {
// Verify startAndDueDateWidget works on create
});
it('should create work item with timeEstimate via two-step approach', async () => {
// 1. Create with timeEstimate parameter
// 2. Verify work item created
// 3. Verify timeEstimate applied via update
});
it('should return partial success when timeEstimate update fails', async () => {
// Mock update to fail
// Verify create succeeded
// Verify _warning in response
});
it('should create work item with all Free tier widgets', async () => {
// assignees, labels, milestone, dates, hierarchy, description
});
it('should create work item with Premium tier widgets', async () => {
// weight, iteration, isFixed
});
it('should create work item with Ultimate tier widgets', async () => {
// healthStatus, color
});
});2. Data Lifecycle Tests
describe('Work Item Data Lifecycle', () => {
describe('Time Tracking Lifecycle', () => {
it('should handle time tracking across lifecycle', async () => {
// 1. Create issue with timeEstimate: "4h" (via two-step)
// 2. Verify timeEstimate applied
// 3. Log timeSpent: "1h 30m"
// 4. Verify totalTimeSpent
// 5. Close issue
// 6. Verify time data preserved
});
});
describe('Dates Lifecycle', () => {
it('should handle date updates including null (clear)', async () => {
// 1. Create with startDate + dueDate
// 2. Verify dates set
// 3. Update dueDate to null
// 4. Verify dueDate cleared, startDate preserved
});
});
describe('Hierarchy Lifecycle', () => {
it('should handle parent relationship changes', async () => {
// 1. Create issue
// 2. Create epic as parent
// 3. Set parent
// 4. Change parent to another epic
// 5. Set parentId to null (unlink)
});
});
describe('Linked Items Lifecycle', () => {
it('should handle work item links', async () => {
// 1. Create issue A and B
// 2. Add link A BLOCKS B
// 3. Verify link exists
// 4. Remove link
// 5. Verify link removed
});
});
});3. AI Agent Experience Tests
describe('AI Agent Error Messages', () => {
it('should return clear partial success response', async () => {
// Create with timeEstimate where update fails
// Verify:
// 1. Work item is created (id, iid, webUrl present)
// 2. _warning explains what failed
// 3. Agent can understand and report to user
});
it('should provide tier-specific guidance on failure', async () => {
// Try to use Premium widget on Free tier
// Verify error in _warning.failedProperties explains tier requirement
});
});Schema Description Updates
Update parameter description for AI agents:
timeEstimate: z
.string()
.optional()
.describe('Time estimate (e.g. "1h 30m", "2d"). Applied via update after create. Check _warning in response if application failed.')References
- GitLab MR !143507 - Add time tracking on create work item
- GitLab Work Items Widgets Documentation
- Epic Work Items API Migration Guide
Acceptance Criteria
-
timeEstimateon create uses two-step approach (create + update) - Partial success returns
_warningwith failed properties - Integration tests cover all widget combinations
- Data lifecycle tests for time tracking, dates, hierarchy, links
- Parameter descriptions updated for AI agents
- ALL widgets verified for create mutation compatibility
- Same pattern applied to any other widgets that fail on create