镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

A list item field with value 'PT' being converted to Datetime object #1340

Open

Description

Describe the bug

I am trying to iterate through a SharePoint list entries/rows for further processing:
...
list = await client.sites.by_site_id(site_id).lists.by_list_id(list_id).items.get(request_configuration=request_config)

rows = []
for item_id in list.value:
fields = item_id['fields']
rows.append(fields.additional_data.get("code")) # test
rows

but when text/string field have value 'PT' it is retrieved as a datetime (exactly: "datetime.timedelta(0)"). It makes my code useless for keeping for example 'Country Code' as list's field. I have seen similar issue before with 4-digit strings, so maybe it is related.

Please, take a moment to look at this.

here some screenshots:

list in SP (just field 'code' and its values) :

Image

and my code results:

Image

Expected behavior

Expected behavior would be to display 'code' the same way as it is in SharePoint list, means:
PT
PTA
..

How to reproduce

To reproduce please just create new list with text field. Then add value 'PT' to this field and try to use await client.sites.by_site_id(site_id).lists.by_list_id(list_id).items.get(request_configuration=request_config) for entried reading.

SDK Version

1.40.0

Latest version known to work for scenario above?

No response

Known Workarounds

No response

Debug output

Click to expand log ```
</details>


### Configuration

_No response_

### Other information

_No response_

Activity

  1. mkkubins commented on Jan 27, 2026

    @mkkubins

    I have similar issue that text column is converted to datetime.time:

    Image

    'xxx': datetime.time(10, 0, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=20280))),

  2. smonsalve-IMC commented on Apr 13, 2026

    @smonsalve-IMC

    Similar issue:

    The raw Graph API wire payload is

    "fields@odata.context": "https://graph.microsoft.com/v1.0/$metadata#sites/fields/$entity",
          "fields": {"column_name": "23.085"}
    

    column

    {
                "columnGroup": "Custom Columns",
                "description": "",
                "displayName": "display_name",
                "enforceUniqueValues": false,
                "hidden": false,
                "indexed": false,
                "name": "column_name",
                "readOnly": false,
                "required": false,
                "text": {
                    "allowMultipleLines": false,
                    "appendChangesToExistingText": false,
                    "linesForEditing": 0,
                    "maxLength": 6
                }
            }
    

    but

    item.fields.additional_data["column_name"]
    returns
    datetime.time(23, 0, 0, 85000)
    after Kiota parsing.

    Environment

    • microsoft-kiota-serialization-json 1.9.8
    • msgraph-sdk 1.52.0
    • Python 3.13, Windows
  3. lramosvea commented on May 18, 2026

    @lramosvea
    Contributor

    Hi Tomasz Ratynski (@SirTomRat), smonsalve-IMC , mkkubins could you please share the JSON response you are getting?

  4. SirTomRat commented on May 25, 2026

    @SirTomRat
    Author

    Luis Ramos (@lramosvea)
    json for existing two rows looks like that:
    {'ID': '1', 'fields': FieldValueSet(additional_data={'@odata.etag': '"5674dfd1-047b-4b9d-a49a-4a34c9263872,11"', 'Title': 'aa', 'LinkTitleNoMenu': 'aa', 'Category': 'categpry1', 'code1': datetime.time(1, 0), 'ContentType': 'Item', 'Modified': datetime.datetime(2026, 2, 11, 13, 48, 2, tzinfo=datetime.timezone.utc), 'Created': datetime.datetime(2025, 7, 2, 10, 40, 36, tzinfo=datetime.timezone.utc), 'AuthorLookupId': '13', 'EditorLookupId': '13', '_UIVersionString': datetime.time(11, 0), 'Attachments': False, 'Edit': '', 'LinkTitle': 'aa', 'ItemChildCount': '0', 'FolderChildCount': '0', '_ComplianceFlags': '', '_ComplianceTag': '', '_ComplianceTagWrittenTime': '', '_ComplianceTagUserId': '', 'AppAuthorLookupId': '18'}, id='1', odata_type=None), 'Created By': 'xyz', 'Modified By': 'xyz'}
    {'ID': '2', 'fields': FieldValueSet(additional_data={'@odata.etag': '"765b81af-1e73-4d93-aa80-d2d8bd8f9988,1"', 'Title': 'bb', 'LinkTitleNoMenu': 'bb', 'Category': datetime.timedelta(0), 'code1': 'ff', 'ContentType': 'Item', 'Modified': datetime.datetime(2026, 5, 25, 7, 6, 29, tzinfo=datetime.timezone.utc), 'Created': datetime.datetime(2026, 5, 25, 7, 6, 29, tzinfo=datetime.timezone.utc), 'AuthorLookupId': '13', 'EditorLookupId': '13', '_UIVersionString': '1.0', 'Attachments': False, 'Edit': '', 'LinkTitle': 'bb', 'ItemChildCount': '0', 'FolderChildCount': '0', '_ComplianceFlags': '', '_ComplianceTag': '', '_ComplianceTagWrittenTime': '', '_ComplianceTagUserId': '', 'AppAuthorLookupId': '18', 'AppEditorLookupId': '18'}, id='2', odata_type=None), 'Created By': 'xyz', 'Modified By': 'xyz'}

    for the SharePoint list:
    Image

  5. 56badkarma commented on Jul 18, 2026

    @56badkarma

    Luis Ramos (@lramosvea) We hit the same bug in a production AP-invoicing app, and in our case it caused actual data corruption, not just display errors. We've also traced the root cause to the exact lines in kiota and have a tested fix — details below.

    Real captured wire payload

    Live tenant, PII redacted. InvoiceId is a "Single line of text" column:

    {
      "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#sites('...')/lists('...')/items(fields(InvoiceId))/$entity",
      "@odata.etag": "\"1342017a-ff3b-463c-a6e3-cefaf61836f0,13\"",
      "id": "42151",
      "webUrl": "https://<tenant>.sharepoint.com/sites/<site>/Extraction%20Library/INV%2019%202026_....pdf",
      "fields": {
        "@odata.etag": "\"1342017a-ff3b-463c-a6e3-cefaf61836f0,13\"",
        "InvoiceId": "19:00:00-20:26"
      }
    }
    item.fields.additional_data["InvoiceId"]
    # datetime.time(19, 0, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=12840)))

    That stored value was originally the invoice number 19-2026 (visible in the document filename). Kiota parsed "19-2026" as 19:00 with a UTC offset of -20:26 fabricated from the year fragment "-2026"; the app later stringified the object on an update and the mangled value is now what's persisted. We found three invoices corrupted this way (18-2026, 19-2026, 20-2026). Any invoice number ending in a plausible-offset suffix does this — for us, that's every invoice numbered with the current year.

    Root cause

    JsonParseNode.try_get_anything in microsoft-kiota-serialization-json (json_parse_node.py) speculatively runs every untyped string (i.e. everything landing in additional_data) through five lenient parsers in sequence: datetime_from_iso_format_compat, UUID, parse_timedelta_string, date.fromisoformat, time_from_iso_format_compat.

    The killer is the last one: on Python 3.11+, time.fromisoformat accepts strings like "19-2026" (hour 19, offset -20:26) and "23.085" (23:00:00.085). parse_timedelta_string accepts "PT" (empty ISO-8601 duration → timedelta(0)) and "10:30" (hh:mm). None of these are plausible as JSON wire formats for time values.

    Minimal repro, no Graph call needed (microsoft-kiota-serialization-json 1.9.3, Python 3.12.10):

    from kiota_serialization_json.json_parse_node import JsonParseNode
    
    for v in ["19-2026", "100-012863299", "PT", "10:30", "11.0"]:
        print(repr(v), "->", repr(JsonParseNode(v).try_get_anything(v)))
    '19-2026'       -> datetime.time(19, 0, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=12840)))
    '100-012863299' -> datetime.time(10, 0, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=81056, microseconds=701000)))
    'PT'            -> datetime.timedelta(0)
    '10:30'         -> datetime.timedelta(seconds=37800)
    '11.0'          -> datetime.time(11, 0)
    

    ('11.0' explains the _UIVersionString corruption shown earlier in this thread.)

    The existing __is_four_digit_number / isdigit() guards (added for earlier reports like #653) patch individual symptoms but leave the underlying speculative parsing in place, which is why this class of bug keeps coming back.

    Proposed fix

    The .NET serializer already does this right: JsonParseNode.TryGetAnything only attempts strict DateTime/DateTimeOffset/Guid parses on strings and otherwise returns the string — it never guesses TimeSpan/TimeOnly/DateOnly. Aligning Python with that:

    _ISO_DATETIME_OR_DATE = re.compile(r"^\d{4}-\d{2}-\d{2}([Tt ]\d{2}:\d{2}|$)")
    _CANONICAL_UUID = re.compile(
        r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
    )
    
    # in try_get_anything, replacing the entire string-handling block:
    if isinstance(value, str):
        if _ISO_DATETIME_OR_DATE.match(value):
            try:
                return datetime_from_iso_format_compat(value)
            except ValueError:
                pass
        elif _CANONICAL_UUID.match(value):
            return UUID(value)
        return value

    Verified against both the failure cases and the values that must keep converting:

    input current proposed
    "19-2026" time(19, 0, tz-20:26) "19-2026" ✅
    "100-012863299" time(10, 0, ...) unchanged string ✅
    "PT" timedelta(0) "PT" ✅
    "10:30" timedelta(seconds=37800) "10:30" ✅
    "23.085" time(23, 0, 0, 85000) "23.085" ✅
    "11.0" time(11, 0) "11.0" ✅
    "d41d8cd98f00b204e9800998ecf8427e" UUID(...) unchanged string (fixes microsoft/kiota-python#487)
    "2026-02-11T13:48:02Z" datetime(...) datetime(...) — unchanged
    "2026-02-11" datetime(2026, 2, 11, 0, 0) datetime(2026, 2, 11, 0, 0) — unchanged
    "9f614123-06fb-...-5bb014d7d3c4" UUID(...) UUID(...) — unchanged

    Notes:

    • Routing date-only strings through datetime_from_iso_format_compat deliberately preserves the current datetime (not date) result, so real Graph payloads (Created, Modified, date columns) behave identically — the only behavior change is that strings which were never date/time/duration values stay strings.
    • The timedelta guess has to go entirely: Graph never sends bare durations in untyped additional_data, and ISO-8601 durations (P.../PT...) overlap with too many real-world strings.
    • The __is_four_digit_number and isdigit() special cases become unnecessary (all-digit strings can't match either anchored pattern) and can be deleted.
    • This also resolves try_get_anything - converts a 32 hex string to UUID microsoft/kiota-python#487 (32-hex → UUID) by requiring the canonical hyphenated form.
    • Typed model fields are unaffected — they go through declared field deserializers and never reach try_get_anything.

    Happy to open a PR against microsoft/kiota-python with this change and tests if the maintainers agree with the direction.

    Environment: msgraph-sdk 1.37.0, msgraph-core 1.3.4, microsoft-kiota-serialization-json 1.9.3, Python 3.12.10. The same code is present on kiota-python main today.

    Related: #653 (2024, closed without addressing the root cause), microsoft/kiota-python#487.

  6. 56badkarma commented on Jul 18, 2026

    @56badkarma

    Follow-up: opened microsoft/kiota-python#664 with the fix described above (strict anchored date/datetime/UUID matching in try_get_anything, mirroring the .NET serializer) plus regression tests covering every corruption pattern reported in this thread.

  7. 56badkarma commented on Aug 19, 2026

    @56badkarma

    microsoft/kiota-python/pull/664
    merged and released last week. I would reccomend closing this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions