openai/openai-python · error · ValueError

Expected a non-empty value for `skill_id` but received {skil

Error message

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

What it means

Sync skill-version creation (POST /skills/{skill_id}/versions) validates skill_id before building the multipart body with 'default' and 'files'. The guard fires before any file reading or deepcopy_with_paths processing, so no bytes are wasted on an invalid target skill.

Source

Thrown at src/openai/resources/skills/versions/versions.py:98

    ) -> SkillVersion:
        """
        Create a new immutable skill version.

        Args:
          default: Whether to set this version as the default.

          files: Skill files to upload (directory upload) or a single zip file.

          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 skill_id:
            raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}")
        body = deepcopy_with_paths(
            {
                "default": default,
                "files": files,
            },
            [["files", "<array>"], ["files"]],
        )
        extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", "<array>"], ["files"]])
        if extracted_files:
            # It should be noted that the actual Content-Type header that will be
            # sent to the server will contain a `boundary` parameter, e.g.
            # multipart/form-data; boundary=---abc--
            extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
        return self._post(
            path_template("/skills/{skill_id}/versions", skill_id=skill_id),
            body=maybe_transform(body, version_create_params.VersionCreateParams),
            files=extracted_files,
            options=make_request_options(

View on GitHub (pinned to 9917c6e28e)

Solutions

  1. Set the target skill ID from client.skills.create() or an existing skill's id
  2. Fail fast if os.environ.get('SKILL_ID') is empty in the script
  3. Dry-run print the parameters before upload

Example fix

# before
v = client.skills.versions.create(skill_id="", files=[...])

# after
skill_id = os.environ["SKILL_ID"]
v = client.skills.versions.create(skill_id=skill_id, files=[...])
Defensive patterns

Strategy: validation

Validate before calling

skill_id = os.environ.get("SKILL_ID", "")
if not skill_id.strip():
    raise SystemExit("SKILL_ID must be set to a valid skill id")
client.skills.versions.create(skill_id=skill_id, files=files)

Type guard

def is_valid_skill_id(value: object) -> bool:
    return isinstance(value, str) and bool(value.strip())

Prevention

When it happens

Trigger: Calling client.skills.versions.create(skill_id="", files=[...]) — uploading a new version to an ID that was never set, None, or an empty string.

Common situations: Upload scripts parameterized by env/config where SKILL_ID is unset; CI publishing pipelines that forgot to export the variable; template code with a placeholder "".

Related errors


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