GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — A list item field with value 'PT' being converted to Datetime object · Issue #1340 · microsoftgraph/msgraph-sdk-python

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

New issue
Copy link
New issue
Copy link
Open
Open
A list item field with value 'PT' being converted to Datetime object#1340
Copy link
Labels
Needs: Attention 👋type:bugA broken experienceA broken experience

Description

@SirTomRat
Tomasz Ratynski (SirTomRat)
opened on Sep 8, 2025
Issue body actions

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. Tomasz Ratynski (SirTomRat)
    added
    status:waiting-for-triageAn issue that is yet to be reviewed or assignedAn issue that is yet to be reviewed or assigned
    type:bugA broken experienceA broken experience
    on Sep 8, 2025
  2. mkkubins commented on Jan 27, 2026

    @mkkubins
    Marta Kubinska (mkkubins)
    on Jan 27, 2026
    More actions

    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))),

  3. smonsalve-IMC commented on Apr 13, 2026

    @smonsalve-IMC
    smonsalve-IMC
    on Apr 13, 2026
    More actions

    Similar issue:

    The raw Graph API wire payload is

    "[email protected]": "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
  4. lramosvea commented on May 18, 2026

    @lramosvea
    Luis Ramos (lramosvea)
    on May 18, 2026
    Contributor
    More actions

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

  5. Luis Ramos (lramosvea)
    added
    status:waiting-for-author-feedbackIssue that we've responded but needs author feedback to closeIssue that we've responded but needs author feedback to close
    and removed
    status:waiting-for-triageAn issue that is yet to be reviewed or assignedAn issue that is yet to be reviewed or assigned
    on May 18, 2026
  6. microsoft-github-policy-service
    added
    Status: No Recent Activity
    on May 25, 2026
  7. SirTomRat commented on May 25, 2026

    @SirTomRat
    Tomasz Ratynski (SirTomRat)
    on May 25, 2026
    Author
    More actions

    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

  8. microsoft-github-policy-service
    added
    Needs: Attention 👋
    and removed
    status:waiting-for-author-feedbackIssue that we've responded but needs author feedback to closeIssue that we've responded but needs author feedback to close
    Status: No Recent Activity
    on May 25, 2026
  9. 56badkarma commented on Jul 18, 2026

    @56badkarma
    Michael (56badkarma)
    on Jul 18, 2026
    More actions

    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.

  10. 56badkarma commented on Jul 18, 2026

    @56badkarma
    Michael (56badkarma)
    on Jul 18, 2026
    More actions

    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.

  11. 56badkarma commented on Aug 19, 2026

    @56badkarma
    Michael (56badkarma)
    on Aug 19, 2026
    More actions

    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

    Labels

    Needs: Attention 👋type:bugA broken experienceA broken experience

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402520 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.