Skip to content

fix: timeTrackingWidget not supported on WorkItemCreateInput - GitLab API limitation #193

@polaz

Description

@polaz

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

  1. 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.

  2. Epic Work Items API Migration Guide lists supported widgets for creation:

    • colorWidget
    • descriptionWidget
    • healthStatusWidget
    • startAndDueDateWidget
    • assigneesWidget

    timeTrackingWidget is 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:

  1. Create work item WITHOUT timeTrackingWidget
  2. Update immediately with timeEstimate using the working update mechanism
  3. 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

Acceptance Criteria

  • timeEstimate on create uses two-step approach (create + update)
  • Partial success returns _warning with 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions