Skip to content

[Bug] Whole-number Double search attributes are missing from list_workflows() typed search attributes #1956

Closed

Description

@RaphaelFakhri

What are you really trying to do?

Read a Double (float) search attribute from the results of client.list_workflows() through typed_search_attributes.

Describe the bug

When a Double search attribute holds a whole number such as 5.0, the visibility API returns its payload as the JSON integer 5. decode_typed_search_attributes() decodes that as a Python int, then drops the pair because int is not the float origin type of the key. The attribute silently disappears from WorkflowExecution.typed_search_attributes.

describe() is not affected because it echoes the payload the client sent (5.0). The same drop applies to any other path that decodes a payload written as 5, for example a workflow started by an SDK whose JSON encoder writes whole-number doubles without a decimal point.

Minimal Reproduction

import asyncio
import uuid

from temporalio.common import (
    SearchAttributeKey,
    SearchAttributePair,
    TypedSearchAttributes,
)
from temporalio.testing import WorkflowEnvironment

score = SearchAttributeKey.for_float("score")


async def main() -> None:
    async with await WorkflowEnvironment.start_local(search_attributes=[score]) as env:
        workflow_id = f"wf-{uuid.uuid4()}"
        handle = await env.client.start_workflow(
            "SomeWorkflow",
            id=workflow_id,
            task_queue="unused",
            search_attributes=TypedSearchAttributes([SearchAttributePair(score, 5.0)]),
        )
        print("describe:", (await handle.describe()).typed_search_attributes.get(score))
        while True:
            found = [
                w async for w in env.client.list_workflows(f"WorkflowId = '{workflow_id}'")
            ]
            if found:
                break
            await asyncio.sleep(0.2)
        print("list raw:", found[0].raw_info.search_attributes.indexed_fields["score"].data)
        print("list:", found[0].typed_search_attributes.get(score))


asyncio.run(main())

Output:

describe: 5.0
list raw: b'5'
list: None

Expected list: 5.0. A value such as 5.5 round-trips correctly; only whole numbers are lost.

Environment/Versions

  • SDK: temporalio 1.34.0 and current main
  • Python 3.12, Linux x86_64
  • Server: Temporal CLI dev server started by WorkflowEnvironment.start_local()

Activity

  1. tconley1428 commented on Oct 11, 2026

    @tconley1428
    Contributor

    Is this an issue you are actually running into with a user scenario, or are you looking for potential bugs to fix?

  2. RaphaelFakhri commented on Oct 13, 2026

    @RaphaelFakhri
    ContributorAuthor

    I found it while reading the search attribute converter, not from a production incident. The repro is real, though: against the dev server, a Double attribute set to 5.0 comes back as 5.0 from describe() and as None from list_workflows(). Anyone who lists workflows and reads a whole-number Double gets a missing value with no error. Priority is your call; #1957 has the fix if you want it.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions