{"record":{"id":"ab9f5ad21e06123f","repo":"google/python-fire","slug":"findexecutableonpath-pathext-0-failed-b","errorCode":null,"errorMessage":"_FindExecutableOnPath(..., pathext='{0}') failed because pathext must be an iterable of strings, but got a string.","messagePattern":"_FindExecutableOnPath\\(\\.\\.\\., pathext='(.+?)'\\) failed because pathext must be an iterable of strings, but got a string\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fire/console/files.py","lineNumber":50,"sourceCode":"\ndef _FindExecutableOnPath(executable, path, pathext):\n  \"\"\"Internal function to a find an executable.\n\n  Args:\n    executable: The name of the executable to find.\n    path: A list of directories to search separated by 'os.pathsep'.\n    pathext: An iterable of file name extensions to use.\n\n  Returns:\n    str, the path to a file on `path` with name `executable` + `p` for\n      `p` in `pathext`.\n\n  Raises:\n    ValueError: invalid input.\n  \"\"\"\n\n  if isinstance(pathext, str):\n    raise ValueError('_FindExecutableOnPath(..., pathext=\\'{0}\\') failed '\n                     'because pathext must be an iterable of strings, but got '\n                     'a string.'.format(pathext))\n\n  # Prioritize preferred extension over earlier in path.\n  for ext in pathext:\n    for directory in path.split(os.pathsep):\n      # Windows can have paths quoted.\n      directory = directory.strip('\"')\n      full = os.path.normpath(os.path.join(directory, executable) + ext)\n      # On Windows os.access(full, os.X_OK) is always True.\n      if os.path.isfile(full) and os.access(full, os.X_OK):\n        return full\n  return None\n\n\ndef _PlatformExecutableExtensions(platform):\n  if platform == platforms.OperatingSystem.WINDOWS:\n    return ('.exe', '.cmd', '.bat', '.com', '.ps1')","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/google/python-fire/blob/716bbc23d7eca949fdb682172283c8d18f742cb6/fire/console/files.py#L32-L68","documentation":"In fire/console/files.py, _FindExecutableOnPath validates that its pathext argument is an iterable of strings (like os.pathext defaults ['.COM','.EXE',...]). If a single string is passed, Python would silently iterate it character-by-character, so the library raises ValueError to surface the likely bug (a string instead of a list).","triggerScenarios":"Calling _FindExecutableOnPath(name, path=..., pathext='.EXE') — passing pathext as a bare string rather than a list/tuple such as ['.EXE']. This typically happens on Windows-style executable resolution.","commonSituations":"Hard-coding a single PATHEXT entry without wrapping it in a list; reading PATHEXT and passing the whole joined string instead of os.environ['PATHEXT'].split(os.pathsep).","solutions":["Wrap the extension(s) in a list: pathext=['.EXE'].","Split the PATHEXT env var: pathext=os.environ.get('PATHEXT', '').split(os.pathsep).","Call the public wrapper FindExecutableOnPath, which supplies sane pathext defaults."],"exampleFix":"// before\n_FindExecutableOnPath('git', path, pathext='.EXE')\n// after\n_FindExecutableOnPath('git', path, pathext=['.EXE'])","handlingStrategy":"type-guard","validationCode":"if isinstance(pathext, str):\n    pathext = pathext.split(os.pathsep)  # or raise before calling","typeGuard":"def is_string_iterable(pathext) -> bool:\n    return not isinstance(pathext, str) and all(isinstance(x, str) for x in pathext)","tryCatchPattern":"try:\n    exe = files._FindExecutableOnPath(name, path, pathext)\nexcept ValueError as e:\n    print(f'pathext must be a list of strings: {e}'); sys.exit(2)","preventionTips":["Always pass pathext as a list/tuple, e.g. ['.EXE'].","Split the PATHEXT env var on os.pathsep before passing.","Prefer the public FindExecutableOnPath wrapper, which defaults pathext safely."],"tags":["python","valueerror","windows","executable-lookup"],"backgroundTag":"invalid-argument-type","analyzedSha":"716bbc23d7eca949fdb682172283c8d18f742cb6","analyzedAt":"2026-08-28T21:57:47.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}