openai/openai-python · error · ValueError

Expected a non-empty value for `run_id` but received {run_id

Error message

Expected a non-empty value for `run_id` but received {run_id!r}

What it means

`steps.retrieve` validates `run_id` (second check) and raises this ValueError when it is falsy, because the run ID is required in the step retrieval path. The check happens before any network activity.

Source

Thrown at src/openai/resources/beta/threads/runs/steps.py:86

              supported value is `step_details.tool_calls[*].file_search.results[*].content`
              to fetch the file search result content.

              See the
              [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings)
              for more information.

          extra_headers: Send extra headers

          extra_query: Add additional query parameters to the request

          extra_body: Add additional JSON properties to the request

          timeout: Override the client-level default timeout for this request, in seconds
        """
        if not thread_id:
            raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}")
        if not run_id:
            raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}")
        if not step_id:
            raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}")
        extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})}
        return self._get(
            path_template(
                "/threads/{thread_id}/runs/{run_id}/steps/{step_id}",
                thread_id=thread_id,
                run_id=run_id,
                step_id=step_id,
            ),
            options=make_request_options(
                extra_headers=extra_headers,
                extra_query=extra_query,
                extra_body=extra_body,
                timeout=timeout,
                query=maybe_transform({"include": include}, step_retrieve_params.StepRetrieveParams),
                security={"bearer_auth": True},
            ),

View on GitHub (pinned to 9917c6e28e)

Solutions

  1. Persist the run ID with every step ID you intend to fetch later
  2. Validate `run_id` before the call
  3. Avoid reusing variable names across loop iterations when building SDK calls

Example fix

# before
step = client.beta.threads.runs.steps.retrieve(thread_id=thread_id, run_id=last_run, step_id="step_abc")  # last_run overwritten to None

# after
run_id_to_use = run.id
step = client.beta.threads.runs.steps.retrieve(thread_id=thread_id, run_id=run_id_to_use, step_id="step_abc")
Defensive patterns

Strategy: validation

Validate before calling

if not run_id:
    raise ValueError("run_id required to retrieve a run step")
step = client.beta.threads.runs.steps.retrieve(thread_id=thread_id, run_id=run_id, step_id=step_id)

Type guard

def is_valid_run_id(value: object) -> TypeGuard[str]:
    return isinstance(value, str) and value.startswith("run_")

Try / catch

try:
    step = client.beta.threads.runs.steps.retrieve(thread_id=thread_id, run_id=run_id, step_id=step_id)
except ValueError as e:
    if "run_id" in str(e):
        logger.warning("step lookup skipped: run id missing")
    else:
        raise

Prevention

When it happens

Trigger: Retrieving a run step with `run_id=None` or `""` — commonly when only the step ID was persisted, or the run variable was overwritten in a loop.

Common situations: Crawling steps from logs where only step IDs were recorded, or variable shadowing (`run` reused for iterations) leaving the wrong value in scope.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28). Data as JSON: /api/errors/21cb88e5e6edeff4. Report an issue: GitHub.