{"record":{"id":"34b78bc34ef04889","repo":"apache/beam","slug":"unable-to-identify-callable-from-r","errorCode":null,"errorMessage":"Unable to identify callable from %r","messagePattern":"Unable to identify callable from %r","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/utils/python_callable.py","lineNumber":100,"sourceCode":"        if line.strip() and line.strip()[0] != '#'\n    ]\n    common_indent = min(len(line) - len(line.lstrip()) for line in lines)\n    lines = [line[common_indent:] for line in lines]\n\n    if method_name is None:\n      for ix, line in reversed(list(enumerate(lines))):\n        if line[0] != ' ':\n          if line.startswith('def '):\n            method_name = line[4:line.index('(')].strip()\n          elif line.startswith('class '):\n            method_name = line[5:line.index('(') if '(' in\n                               line else line.index(':')].strip()\n          else:\n            method_name = '__python_callable__'\n            lines[ix] = method_name + ' = ' + line\n          break\n      else:\n        raise ValueError(\"Unable to identify callable from %r\" % source)\n\n    # pylint: disable=exec-used\n    # pylint: disable=ungrouped-imports\n    import apache_beam as beam\n    exec_globals = {'beam': beam}\n    exec('\\n'.join(lines), exec_globals)\n    return exec_globals[method_name]\n\n  def default_label(self):\n    src = self._source.strip()\n    last_line = src.split('\\n')[-1]\n    if last_line[0] != ' ' and len(last_line) < 72:\n      return last_line\n    # Avoid circular import.\n    from apache_beam.transforms.ptransform import label_from_callable\n    return label_from_callable(self._callable)\n\n  @property","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/utils/python_callable.py#L82-L118","documentation":"load_from_script parses a Python source string to find a callable assigned to a variable (or def) to exec. If no assignment/def line is found after scanning all lines, Beam raises ValueError because there is no callable to extract from the source.","triggerScenarios":"Calling load_from_script(source) or load_from_source(source) with a string containing no top-level assignment or function definition (e.g. only an expression like 'lambda x: x' with no 'name = ' prefix, or only imports).","commonSituations":"Passing a lambda expression directly instead of 'fn = lambda x: ...'; passing a fully-qualified callable name instead of source code; whitespace/comment-only strings.","solutions":["Wrap the expression in an assignment: 'my_callable = lambda x: x*2'","Or use a def: 'def my_callable(x): return x*2'","If you already have a callable object, do not serialize via source — pass it directly"],"exampleFix":"// before\nload_from_script('lambda x: x * 2')\n// after\nload_from_script('double = lambda x: x * 2')","handlingStrategy":"validation","validationCode":"def is_valid_callable_source(source: str) -> bool:\n    import ast\n    try:\n        tree = ast.parse(source)\n    except SyntaxError:\n        return False\n    return any(isinstance(n, (ast.Assign, ast.FunctionDef)) for n in tree.body)","typeGuard":null,"tryCatchPattern":"try:\n    fn = load_from_script(source)\nexcept ValueError as e:\n    logging.error('no callable in source: %s', e)\n    raise","preventionTips":["Always define callables as 'name = <expr>' or 'def name(...): ...'","Do not pass bare lambda expressions or import-only strings","Sanity-check the source parses with ast.parse before submission"],"tags":["python","exec","source-parsing"],"backgroundTag":"invalid-argument-format","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}