{"record":{"id":"a3e5192ab7555adc","repo":"binary-husky/gpt_academic","slug":"file","errorCode":null,"errorMessage":"文件{file}不存在","messagePattern":"文件\\{file\\}不存在","errorType":"exception","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"toolbox.py","lineNumber":428,"sourceCode":"        child_path = os.path.abspath(file)\n        if os.path.samefile(os.path.commonpath([parent_path, child_path]), parent_path):\n            return True\n        else:\n            return False\n    except:\n        return False\n\n\ndef promote_file_to_downloadzone(file:str, rename_file:str=None, chatbot:ChatBotWithCookies=None):\n    # 将文件复制一份到下载区\n    import shutil\n\n    if chatbot is not None:\n        user_name = get_user(chatbot)\n    else:\n        user_name = default_user_name\n    if not os.path.exists(file):\n        raise FileNotFoundError(f\"文件{file}不存在\")\n    user_path = get_log_folder(user_name, plugin_name=None)\n    if file_already_in_downloadzone(file, user_path):\n        new_path = file\n    else:\n        user_path = get_log_folder(user_name, plugin_name=\"downloadzone\")\n        if rename_file is None:\n            rename_file = f\"{gen_time_str()}-{os.path.basename(file)}\"\n        new_path = pj(user_path, rename_file)\n        # 如果已经存在，先删除\n        if os.path.exists(new_path) and not os.path.samefile(new_path, file):\n            os.remove(new_path)\n        # 把文件复制过去\n        if not os.path.exists(new_path):\n            shutil.copyfile(file, new_path)\n    # 将文件添加到chatbot cookie中\n    if chatbot is not None:\n        if \"files_to_promote\" in chatbot._cookies:\n            current = chatbot._cookies[\"files_to_promote\"]","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/binary-husky/gpt_academic/blob/d6bde0fa54373309bd05823a49bda8da019d2c77/toolbox.py#L410-L446","documentation":"Raised by promote_file_to_downloadzone in toolbox.py:428 when the file path passed in does not exist on disk. The function copies a generated file into the per-user downloadzone folder so the Gradio UI can offer it as a download; the os.path.exists check at the top is a fail-fast guard before any copying happens. Seeing it means a plugin produced (or claimed to produce) a file path that was never actually written or was already deleted.","triggerScenarios":"Calling promote_file_to_downloadzone(file, ...) where file points to a path that was never created (upstream step failed silently), a file deleted by a cleanup step between generation and promotion, or a relative path resolved against an unexpected working directory. Common with plugins that build an output path with pj(...) but skip writing when the LLM/tool output is empty.","commonSituations":"A plugin's render/export step failed (e.g. LLM returned nothing) so the output .md/.pdf/.tex file was never generated, but the promotion call still runs. Log-folder cleanup or a restarted backend removed temp files. Path built from user-supplied filename containing illegal characters so the earlier write failed.","solutions":["Check with os.path.exists / ls whether the upstream step actually wrote the file, and fix the generation step that failed.","If the file was written, verify the path passed to promote_file_to_downloadzone is absolute or resolved relative to the same base directory used at write time (use pj(get_log_folder(...), name) consistently).","Guard the call: only promote when the file exists, otherwise surface a user-facing message in chatbot instead of crashing.","If files are vanishing between steps, inspect concurrent cleanup logic (log folder pruning) and promote before cleanup or write to a stable location."],"exampleFix":"# before\npromote_file_to_downloadzone(res_path, chatbot=chatbot)  # crashes if render failed\n\n# after\nif os.path.exists(res_path):\n    promote_file_to_downloadzone(res_path, chatbot=chatbot)\nelse:\n    chatbot.append((None, f\"[local] 生成失败，未找到文件: {res_path}\"))","handlingStrategy":"type-guard","validationCode":"import os\n\ndef safe_promote(file: str, chatbot=None, rename_file: str = None):\n    if not file or not os.path.isfile(os.path.abspath(file)):\n        return None  # caller decides how to report\n    promote_file_to_downloadzone(file, rename_file=rename_file, chatbot=chatbot)\n    return file","typeGuard":"def is_promotable_file(file) -> bool:\n    return isinstance(file, str) and len(file) > 0 and os.path.isfile(file)","tryCatchPattern":"try:\n    promote_file_to_downloadzone(res_path, chatbot=chatbot)\nexcept FileNotFoundError as e:\n    # upstream generation failed; tell the user instead of crashing the plugin\n    chatbot.append((None, f'[local] output file missing, generation step likely failed: {e}'))\n    yield from update_chatbot(chatbot)","preventionTips":["Check os.path.isfile on every generated artifact immediately before promoting it.","Write outputs with absolute paths derived from get_log_folder(...) so write and promote resolve identically.","Treat a missing output file as an upstream failure: log the generating step's exit status, don't just guard the promotion call."],"tags":["file-io","plugin-pipeline","download","validation"],"backgroundTag":null,"analyzedSha":"d6bde0fa54373309bd05823a49bda8da019d2c77","analyzedAt":"2026-08-14T22:48:35.038Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}