apache/beam · error · ValueError

File " " is not a valid .py file.

Error message

File "{path}" is not a valid .py file.

What it means

The Beam YAML MapToFields transform raises this when a Python UDF is specified via both `path` and `name` but the file path does not end with '.py'. The transform loads the script with PythonCallableWithSource.load_from_script and requires a .py extension as a sanity check.

Solutions

  1. Ensure the `path` points to a plain-text file ending in '.py' containing the callable named by `name`.
  2. Extract the function into a .py file if it currently lives in a notebook or module.
  3. Alternatively use the inline `expression` or `callable` config options instead of `path`.

Example fix

# before
config:
  path: transforms/udf.pyc
  name: my_fn
# after
config:
  path: transforms/udf.py
  name: my_fn
Defensive patterns

Strategy: validation

Validate before calling

assert path.endswith('.py'), f'File "{path}" is not a valid .py file.'

Type guard

def is_python_udf_spec(cfg: dict) -> bool:
    return isinstance(cfg.get('path'), str) and cfg['path'].endswith('.py') and bool(cfg.get('name'))

Try / catch

try:
    out = run_pipeline(spec)
except ValueError as e:
    if 'is not a valid .py file' in str(e):
        spec['config']['path'] = spec['config']['path'] + '.py'
        out = run_pipeline(spec)
    else:
        raise

Prevention

When it happens

Trigger: Calling _expand_python_mapping_func with {'path': '...', 'name': '...'} where path has a non-.py extension, e.g. pointing at a compiled .pyc, a .txt, a module directory, or an IPython notebook .ipynb.

Common situations: Users reference a packaged module, a notebook, a .pyc, or a file with a missing/wrong extension; also happens when copy-pasting config from a JavaScript example and forgetting to change the extension.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/d5a62a37999b21c1. Report an issue: GitHub.

Appendix: source

Thrown at sdks/python/apache_beam/yaml/yaml_mapping.py:308

        }}
      }}
    }}
    return obj;
  }}

  function __wrapper__(row) {{
    return {user_entrypoint}(__convert_dates__(row));
  }}
  """

  return _JsFunctionWrapper(source_code, '__wrapper__')


def _expand_python_mapping_func(
    original_fields, expression=None, callable=None, path=None, name=None):
  if path and name:
    if not path.endswith('.py'):
      raise ValueError(f'File "{path}" is not a valid .py file.')
    py_file = FileSystems.open(path).read().decode()

    return python_callable.PythonCallableWithSource.load_from_script(
        py_file, name)

  elif expression:
    # TODO(robertwb): Consider constructing a single callable that takes
    # the row and returns the new row, rather than invoking (and unpacking)
    # for each field individually.
    source = '\n'.join(['def fn(__row__):'] + [
        f'  {name} = __row__.{name}'
        for name in original_fields if name in expression
    ] + ['  return (' + expression + ')'])

  else:
    source = callable

  return python_callable.PythonCallableWithSource(source)

View on GitHub (pinned to 12126d8942)