{"record":{"id":"ec3581968fd421da","repo":"xtekky/gpt4free","slug":"file-must-have-pa-py-extension-file-path","errorCode":null,"errorMessage":"File must have .pa.py extension: {file_path}","messagePattern":"File must have \\.pa\\.py extension: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"g4f/mcp/pa_provider.py","lineNumber":748,"sourceCode":"    with a ``create_completion`` or ``create_async_generator`` attribute is\n    returned instead.\n\n    Args:\n        file_path: Path to the ``.pa.py`` file.\n\n    Returns:\n        The provider class, or ``None`` if none could be found.\n\n    Raises:\n        FileNotFoundError: If *file_path* does not exist.\n        ValueError: If *file_path* does not end with ``.pa.py``.\n        RuntimeError: If the file fails to execute.\n    \"\"\"\n    file_path = Path(file_path)\n    if not file_path.exists():\n        raise FileNotFoundError(f\"PA provider file not found: {file_path}\")\n    if not file_path.name.endswith(\".pa.py\"):\n        raise ValueError(f\"File must have .pa.py extension: {file_path}\")\n\n    code = file_path.read_text(encoding=\"utf-8\")\n    result = execute_safe_code(code, file_path=file_path, timeout=0.1, max_depth=100)\n\n    if not result.success:\n        raise RuntimeError(\n            f\"Failed to load PA provider from {file_path}:\\n{result.error}\"\n        )\n\n    # Prefer an explicit 'Provider' name\n    provider_class = result.locals.get(\"Provider\")\n    if provider_class is not None:\n        return provider_class\n\n    # Fall back to any class that looks like a provider\n    for obj in result.locals.values():\n        if isinstance(obj, type) and (\n            hasattr(obj, \"create_completion\") or hasattr(obj, \"create_async_generator\")","sourceCodeStart":730,"sourceCodeEnd":766,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/mcp/pa_provider.py#L730-L766","documentation":"Raised by load_pa_provider when the file exists but its name does not end with '.pa.py'. The double extension is the convention that marks a file as a sandboxed PA provider; plain '.py' files are rejected so they cannot be confused with trusted code.","triggerScenarios":"Passing 'myprov.py', 'provider.txt' or any file whose Path.name doesn't end with '.pa.py' to load_pa_provider.","commonSituations":"Renaming existing provider modules without adding the .pa.py suffix; automation that strips or rewrites extensions; copy-paste from examples that used plain .py files.","solutions":["Rename the file to end in .pa.py (e.g. myprov.pa.py)","Validate the suffix in your own tooling before calling load: p.name.endswith('.pa.py')"],"exampleFix":"# before\nload_pa_provider(ws / 'myprov.py')\n\n# after\nload_pa_provider(ws / 'myprov.pa.py')","handlingStrategy":"validation","validationCode":"from pathlib import Path\nassert Path(file_path).name.endswith('.pa.py'), \"PA providers must end in .pa.py\"","typeGuard":null,"tryCatchPattern":"try:\n    provider_cls = load_pa_provider(path)\nexcept ValueError:\n    path = path.with_name(path.stem + '.pa.py')  # or reject the registration","preventionTips":["Enforce the .pa.py suffix at registration time in your tooling","Treat the double extension as the trust marker it is — do not auto-rename silently in production"],"tags":["pa-provider","validation","file-extension"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}