Skip to content

Bug Report: JSON Unmarshal Error in SubscriptionsAPI - Quantity Field Type Mismatch #4

@maasumiyaat

Description

@maasumiyaat

Description

The SDK fails to parse subscription responses from the SubscriptionsIdGet endpoint due to a type mismatch in the quantity field of SubscriptionLineItem.

Error Message

json: cannot unmarshal string into Go struct field SubscriptionSubscriptionLineItem.line_items.quantity of type float32

Expected Behavior

The SDK should successfully parse subscription responses, correctly handling the quantity field in line items regardless of whether the API returns it as a string or number.

Actual Behavior

The SDK crashes when attempting to unmarshal subscription data because the SubscriptionLineItem.Quantity field is defined as float32, but the API returns it as a string (e.g., "10" instead of 10).

Steps to Reproduce

resp, httpResp, err := apiClient.SubscriptionsAPI.SubscriptionsIdGet(ctx, subscriptionID).Execute()

// Error occurs during response parsing when quantity is a string

Root Cause

The SDK's subscription line item model defines:

type SubscriptionLineItem struct {
    Quantity float32 `json:"quantity"`
}

However, the API returns:

{
  "line_items": [
    {
      "quantity": "10"  // String, not number
    }
  ]
}

Proposed Solution

Option 1: Change field type to handle both (Recommended)

type SubscriptionLineItem struct {
    Quantity json.Number `json:"quantity"`
}

// Add helper method
func (s *SubscriptionLineItem) GetQuantityFloat() (float32, error) {
    if s.Quantity == "" {
        return 0, nil
    }
    f, err := s.Quantity.Float64()
    return float32(f), err
}

Option 2: Custom unmarshaling

type SubscriptionLineItem struct {
    Quantity float32 `json:"-"`
}

func (s *SubscriptionLineItem) UnmarshalJSON(data []byte) error {
    type Alias SubscriptionLineItem
    aux := &struct {
        Quantity interface{} `json:"quantity"`
        *Alias
    }{
        Alias: (*Alias)(s),
    }
    
    if err := json.Unmarshal(data, &aux); err != nil {
        return err
    }
    
    switch v := aux.Quantity.(type) {
    case string:
        fmt.Sscanf(v, "%f", &s.Quantity)
    case float64:
        s.Quantity = float32(v)
    }
    
    return nil
}

Option 3: Use string type with conversion helpers

type SubscriptionLineItem struct {
    Quantity string `json:"quantity"`
}

// Add helper method
func (s *SubscriptionLineItem) GetQuantityFloat() (float32, error) {
    var f float32
    _, err := fmt.Sscanf(s.Quantity, "%f", &f)
    return f, err
}

API Response Example

{
  "id": "sub_123",
  "line_items": [
    {
      "id": "li_456",
      "quantity": "10",
      "price": 99.99
    }
  ]
}

Impact

  • Severity: High
  • Affected Endpoints: SubscriptionsIdGet, potentially SubscriptionsGet and any other endpoint returning subscription line items
  • User Impact: Unable to retrieve subscription data, blocking core functionality

Related Issues

This is similar to inconsistent type handling across the API. Other fields may have the same issue where the API returns strings instead of numbers.

Workaround

Currently, there's no clean workaround since the error occurs during the SDK's internal JSON unmarshaling before user code can access the response. Users must either:

  1. Wait for SDK fix
  2. Fork and modify the SDK
  3. Make raw HTTP requests and parse manually

Checklist

  • Verify OpenAPI spec has correct type for quantity field
  • Check if API is sending inconsistent types (string vs number)
  • Update model definitions to handle both string and number types
  • Regenerate SDK
  • Add tests for type coercion
  • Document the field type behavior
  • Check other numeric fields for similar issues

Additional Context

This type mismatch suggests either:

  1. The API is inconsistently returning different types for the same field
  2. The SDK generation tool doesn't handle type coercion properly

It's recommended to audit all numeric fields across all models to ensure consistent type handling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions