{"record":{"id":"84844015fcad1abe","repo":"pandas-dev/pandas","slug":"must-provide-func-or-tuples-of-column-aggfunc","errorCode":null,"errorMessage":"Must provide 'func' or tuples of '(column, aggfunc).","messagePattern":"Must provide 'func' or tuples of '\\(column, aggfunc\\)\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/apply.py","lineNumber":1946,"sourceCode":"\n    relabeling = func is None and (\n        is_multi_agg_with_relabel(**kwargs)\n        or any(isinstance(v, NamedAgg) for v in kwargs.values())\n    )\n\n    columns: tuple[str, ...] | None = None\n    order: npt.NDArray[np.intp] | None = None\n\n    if not relabeling:\n        if isinstance(func, list) and len(func) > len(set(func)):\n            # GH 28426 will raise error if duplicated function names are used and\n            # there is no reassigned name\n            raise SpecificationError(\n                \"Function names must be unique if there is no new column names assigned\"\n            )\n        if func is None:\n            # nicer error message\n            raise TypeError(\"Must provide 'func' or tuples of '(column, aggfunc).\")\n\n    if relabeling:\n        normalization_needed = False\n        # error: Incompatible types in assignment (expression has type\n        # \"MutableMapping[Hashable, list[Callable[..., Any] | str]]\", variable has type\n        # \"Callable[..., Any] | str | list[Callable[..., Any] | str] |\n        # MutableMapping[Hashable, Callable[..., Any] | str | list[Callable[..., Any] |\n        # str]] | None\")\n        converted_kwargs = {}\n        for key, val in kwargs.items():\n            if isinstance(val, NamedAgg):\n                column = val.column\n                aggfunc = val.aggfunc\n                if val.args or val.kwargs:\n                    aggfunc = lambda x, func=aggfunc, a=val.args, kw=val.kwargs: func(\n                        x, *a, **kw\n                    )\n            else:","sourceCodeStart":1928,"sourceCodeEnd":1964,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/apply.py#L1928-L1964","documentation":"TypeError raised in reconstruct_func when neither a positional func nor any NamedAgg / relabeling kwargs were provided. The agg/apply call has nothing to compute, so pandas surfaces a clearer message than the downstream NoneType error.","triggerScenarios":"df.agg(); df.agg(None); df.groupby('k').agg(None); calling agg with a func variable that resolved to None.","commonSituations":"Programmatic code where the func argument is computed from user input and defaults to None when nothing is selected.","solutions":["Provide at least one positional function: df.agg('sum').","Provide one or more named-aggregation kwargs: df.agg(a='sum').","Guard the call site so that func=None is not passed (skip the call or default to a sensible function)."],"exampleFix":"# before\ndf.agg(None)\n# after\ndf.agg('sum')","handlingStrategy":"validation","validationCode":"def safe_agg(obj, func=None, **kwargs):\n    if func is None and not kwargs:\n        raise ValueError(\"agg requires a func or named-aggregation kwargs\")\n    return obj.agg(func, **kwargs) if func is not None else obj.agg(**kwargs)","typeGuard":null,"tryCatchPattern":"try:\n    df.agg(func)\nexcept TypeError as e:\n    if \"Must provide\" in str(e):\n        df.agg('sum')  # sensible default\n    else:\n        raise","preventionTips":["Default func to a sensible function (e.g. 'sum') when it may resolve to None.","Skip the agg call entirely if no aggregation was requested."],"tags":["agg","validation","argument"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}