Unable to save Assistant after uploading a file to Code Interpreter

Hello,

for the past few weeks we’ve been experiencing issues with the GPT agents defined in the “Assistants” section of OpenAI Playground.

Whenever I upload a file in Tools → Code Interpreter, the file appears as “Uploaded”, but the Assistant cannot be saved. The platform displays the error “Couldn’t save”, and after reloading the page the file is no longer listed (as shown in the image with the red “Couldn’t save” message at the bottom).

Additional details

  • The issue occurs with any file type and size (within documented limits).

  • The Assistant has no other tools or files enabled (e.g., no File Search / Knowledge files).

  • The issue persists across multiple browsers and devices.

  • Other colleagues experience the same behavior on assistants defined within the same Project.

  • No additional error messages are shown besides “Couldn’t save”.

  • The file upload seems to work, since the file is then present in the storage list, but the assistant never gets to be saved with the file attached.

    Thanks.

I have the same problem, any solutions?
1 Like

Hi and welcome to the community!

I tried to reproduce the issue but my results were a bit different. Adding a file to a code interpreter tool works and saves correctly, but removing that same file triggers a “Couldn’t save” error. After that, attaching a new file and saving also fails.

Refreshing the page clears the problem as a workaround.

1 Like

Unfortunately I haven’t found a solution yet.

Hi, in our OpenAI platform project we still cannot attach files to code interpreter at all, even on new assistants created from scratch.

After 10 days and testing from different devices, we are still getting the same error. Is there another way to get help from OpenAI?

By API call, I can attach multiple files to Assistants’ code interpreter when creating or modifying…

The success then seen in the Playground without borking out errors:

I can then trash can a file there, removing its attachment.


That local Python utility, created for testing this symptom? Available to you here.

Requires:

  • Python (newer, like 3.11+)
  • tkinter (standard library with typical desktop install)
  • openai module
  • local environment OPENAI_API_KEY or others scraped by openai lib.
  • save as assistant_maker.pyw, where pyw extension suppresses a console and all the deprecation warnings.
import os
import threading
from dataclasses import dataclass, field

import tkinter as tk
from tkinter import ttk, filedialog, messagebox

import openai


SUPPORTED_EXTENSIONS: set[str] = {
    ".c",
    ".cpp",
    ".cs",
    ".css",
    ".csv",
    ".doc",
    ".docx",
    ".gif",
    ".go",
    ".html",
    ".java",
    ".jpeg",
    ".jpg",
    ".js",
    ".json",
    ".md",
    ".pdf",
    ".php",
    ".pkl",
    ".png",
    ".pptx",
    ".py",
    ".rb",
    ".tar",
    ".tex",
    ".ts",
    ".txt",
    ".webp",
    ".xlsx",
    ".xml",
    ".zip",
}


@dataclass
class FileRecord:
    local_path: str
    filename: str
    openai_file_id: str | None = None
    status: str = "pending"  # pending, uploading, uploaded, error, unsupported, deleted
    attached_current: bool = False
    attached_assistant_ids: set[str] = field(default_factory=set)
    last_error: str | None = None



class AssistantBuilderApp:
    def __init__(self, root: tk.Tk) -> None:
        self.root = root
        self.root.title("OpenAI Assistant Builder")

        api_key = os.getenv("OPENAI_API_KEY")
        if not api_key:
            raise RuntimeError("OPENAI_API_KEY environment variable is not set.")

        self.client = openai.OpenAI(api_key=api_key, timeout=60.0, max_retries=1)

        self.current_assistant_id: str | None = None
        self.file_records: dict[str, FileRecord] = {}
        self.path_index: dict[str, str] = {}

        self._build_ui()

    def _build_ui(self) -> None:
        top = ttk.Frame(self.root, padding=10)
        top.pack(side=tk.TOP, fill=tk.X)

        ttk.Label(top, text="Assistant ID:").pack(side=tk.LEFT)
        self.assistant_id_var = tk.StringVar(value="(new)")
        self.assistant_id_label = ttk.Label(top, textvariable=self.assistant_id_var, width=40)
        self.assistant_id_label.pack(side=tk.LEFT, padx=(4, 10))

        self.new_button = ttk.Button(top, text="New Assistant", command=self.new_assistant)
        self.new_button.pack(side=tk.LEFT, padx=5)

        self.delete_button = ttk.Button(top, text="Delete Assistant", command=self.delete_current_assistant)
        self.delete_button.pack(side=tk.LEFT, padx=5)

        main_pane = ttk.Panedwindow(self.root, orient=tk.HORIZONTAL)
        main_pane.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))

        assistant_frame = ttk.Labelframe(main_pane, text="Assistant Parameters", padding=10)
        files_frame = ttk.Labelframe(main_pane, text="Files for Code Interpreter", padding=10)

        main_pane.add(assistant_frame, weight=1)
        main_pane.add(files_frame, weight=2)

        # Assistant parameters
        row = 0
        ttk.Label(assistant_frame, text="Name:").grid(row=row, column=0, sticky="w")
        self.name_var = tk.StringVar()
        ttk.Entry(assistant_frame, textvariable=self.name_var).grid(row=row, column=1, sticky="ew")
        row += 1

        ttk.Label(assistant_frame, text="Model:").grid(row=row, column=0, sticky="w")
        self.model_var = tk.StringVar(value="gpt-4.1")
        ttk.Entry(assistant_frame, textvariable=self.model_var).grid(row=row, column=1, sticky="ew")
        row += 1

        ttk.Label(assistant_frame, text="Temperature:").grid(row=row, column=0, sticky="w")
        self.temperature_var = tk.StringVar(value="0.5")
        ttk.Entry(assistant_frame, textvariable=self.temperature_var, width=8).grid(
            row=row,
            column=1,
            sticky="w",
        )
        row += 1

        ttk.Label(assistant_frame, text="Top P:").grid(row=row, column=0, sticky="w")
        self.top_p_var = tk.StringVar(value="0.2")
        ttk.Entry(assistant_frame, textvariable=self.top_p_var, width=8).grid(
            row=row,
            column=1,
            sticky="w",
        )
        row += 1

        ttk.Label(assistant_frame, text="Metadata (JSON object):").grid(row=row, column=0, sticky="nw")
        self.metadata_text = tk.Text(assistant_frame, height=4, width=40, wrap="word")
        self.metadata_text.grid(row=row, column=1, sticky="nsew", pady=(0, 5))
        row += 1

        ttk.Label(assistant_frame, text="Instructions:").grid(row=row, column=0, sticky="nw")
        self.instructions_text = tk.Text(assistant_frame, height=8, width=40, wrap="word")
        self.instructions_text.grid(row=row, column=1, sticky="nsew")
        row += 1

        ttk.Label(
            assistant_frame,
            text="Mapping:",
        ).grid(row=row, column=0, sticky="nw")
        auto_frame = ttk.Frame(assistant_frame)
        auto_frame.grid(row=row, column=1, sticky="nsew", pady=(2, 0))
        self.auto_instructions_text = tk.Text(
            auto_frame,
            height=8,
            width=40,
            wrap="word",
            state="disabled",
        )
        auto_scrollbar = ttk.Scrollbar(
            auto_frame,
            orient="vertical",
            command=self.auto_instructions_text.yview,
        )
        self.auto_instructions_text.configure(yscrollcommand=auto_scrollbar.set)
        self.auto_instructions_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        auto_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        row += 1

        tools_frame = ttk.Frame(assistant_frame)
        tools_frame.grid(row=row, column=1, sticky="w", pady=(5, 0))
        self.code_interpreter_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(
            tools_frame,
            text="Enable Code Interpreter",
            variable=self.code_interpreter_var,
            command=self._refresh_auto_instructions_preview,
        ).pack(side=tk.LEFT)
        row += 1

        self.create_update_button = ttk.Button(
            assistant_frame,
            text="Create Assistant",
            command=self.create_or_update_assistant,
        )
        self.create_update_button.grid(row=row, column=1, sticky="e", pady=(10, 0))

        assistant_frame.columnconfigure(1, weight=1)
        # Let metadata, instructions, and auto-mapping areas grow with the window
        assistant_frame.rowconfigure(4, weight=1)  # metadata
        assistant_frame.rowconfigure(5, weight=1)  # instructions
        assistant_frame.rowconfigure(6, weight=1)  # auto mapping

        # Files frame
        btns = ttk.Frame(files_frame)
        btns.pack(fill=tk.X)

        ttk.Button(btns, text="Add Files…", command=self.add_files).pack(side=tk.LEFT)
        ttk.Button(btns, text="Attach/Detach Selected", command=self.toggle_attach_selected).pack(
            side=tk.LEFT,
            padx=(5, 0),
        )
        ttk.Button(
            btns,
            text="Delete Selected From Server",
            command=self.delete_selected_files_from_server,
        ).pack(side=tk.LEFT, padx=(5, 0))
        ttk.Button(
            btns,
            text="Remove From List",
            command=self.remove_selected_from_list,
        ).pack(side=tk.LEFT, padx=(5, 0))

        columns = ("filename", "status", "file_id", "attached", "assistants")
        self.tree = ttk.Treeview(
            files_frame,
            columns=columns,
            show="headings",
            selectmode="extended",
            height=12,
        )
        self.tree.heading("filename", text="Filename")
        self.tree.heading("status", text="Status")
        self.tree.heading("file_id", text="File ID")
        self.tree.heading("attached", text="Attached (current)")
        self.tree.heading("assistants", text="Attached to Assistants")

        self.tree.column("filename", width=220, anchor="w")
        self.tree.column("status", width=80, anchor="w")
        self.tree.column("file_id", width=260, anchor="w")
        self.tree.column("attached", width=110, anchor="center")
        self.tree.column("assistants", width=180, anchor="w")

        self.tree.pack(fill=tk.BOTH, expand=True, pady=(5, 0))
        self.tree.bind("<Double-1>", self.on_tree_double_click)

        # Log frame
        log_frame = ttk.Labelframe(self.root, text="Log", padding=5)
        log_frame.pack(fill=tk.BOTH, expand=False, padx=10, pady=(0, 10))
        self.log_text = tk.Text(log_frame, height=8, wrap="word")
        self.log_text.pack(fill=tk.BOTH, expand=True)

        # Initial preview (blank)
        self._refresh_auto_instructions_preview()
        self.log("Ready. Select files and define assistant parameters.")

    def log(self, message: str) -> None:
        self.log_text.insert("end", message + "\n")
        self.log_text.see("end")

    def new_assistant(self) -> None:
        self.current_assistant_id = None
        self.assistant_id_var.set("(new)")
        self.name_var.set("")
        self.model_var.set("gpt-4.1")
        self.temperature_var.set("0.1")
        self.top_p_var.set("1.0")
        self.metadata_text.delete("1.0", "end")
        self.instructions_text.delete("1.0", "end")
        self.code_interpreter_var.set(True)
        for item_id, record in self.file_records.items():
            record.attached_current = False
            self.update_tree_row(item_id)
        self.create_update_button.configure(text="Create Assistant")
        self._refresh_auto_instructions_preview()
        self.log("Started a new assistant configuration.")


    def delete_current_assistant(self) -> None:
        if not self.current_assistant_id:
            messagebox.showinfo("Delete Assistant", "No assistant is currently loaded.")
            return
        if not messagebox.askyesno(
            "Delete Assistant",
            f"Delete assistant {self.current_assistant_id} from OpenAI?",
        ):
            return

        assistant_id = self.current_assistant_id

        def worker() -> None:
            try:
                self.client.beta.assistants.delete(assistant_id)

                def on_success() -> None:
                    self.log(f"Deleted assistant {assistant_id}.")
                    if self.current_assistant_id == assistant_id:
                        self.current_assistant_id = None
                        self.assistant_id_var.set("(new)")
                        self.create_update_button.configure(text="Create Assistant")

                self.root.after(0, on_success)
            except Exception as e:  # noqa: BLE001
                error_text = str(e)

                def on_error() -> None:
                    self.log(f"Error deleting assistant {assistant_id}:\n{error_text}")
                    messagebox.showerror("Delete Assistant Error", error_text)

                self.root.after(0, on_error)

        threading.Thread(target=worker, daemon=True).start()

    def _update_supported_extensions_from_error(self, error_text: str) -> None:
        import re

        idx = error_text.find("Supported formats:")
        if idx == -1:
            return

        supported_part = error_text[idx:]
        # Extract quoted extensions like "py", "json", etc.
        exts = re.findall(r'"([a-zA-Z0-9]+)"', supported_part)
        if not exts:
            return

        new_set = {f".{ext.lower()}" for ext in exts}
        if not new_set:
            return

        global SUPPORTED_EXTENSIONS
        if new_set != SUPPORTED_EXTENSIONS:
            SUPPORTED_EXTENSIONS = new_set
            self.log(
                "Updated supported extensions from API error: "
                + ", ".join(sorted(SUPPORTED_EXTENSIONS))
            )


    def add_files(self) -> None:
        patterns = " ".join(f"*{ext}" for ext in sorted(SUPPORTED_EXTENSIONS))
        filetypes = [
            ("Supported assistant file types", patterns),
            ("All files", "*.*"),
        ]
        paths = filedialog.askopenfilenames(
            title="Select files for assistants",
            filetypes=filetypes,
        )
        if not paths:
            return

        unsupported_names: list[str] = []

        for path in paths:
            if not path:
                continue
            if path in self.path_index:
                self.log(f"File already in list: {path}")
                continue

            filename = os.path.basename(path)
            ext = os.path.splitext(filename)[1].lower()

            record = FileRecord(local_path=path, filename=filename)

            # Pre-filter unsupported extensions so we don't even attempt upload
            if not ext or ext not in SUPPORTED_EXTENSIONS:
                record.status = "unsupported"
                record.attached_current = False
            else:
                # NEW: default to attached for supported files
                record.attached_current = True

            attached_value = "Yes" if record.attached_current else "No"

            item_id = self.tree.insert(
                "",
                "end",
                values=(record.filename, record.status, "", attached_value, ""),
            )
            self.file_records[item_id] = record
            self.path_index[path] = item_id

            if record.status == "unsupported":
                unsupported_names.append(record.filename)
                self.log(f"Unsupported file type (not uploaded): {record.filename}")
            else:
                # Supported: begin upload; once complete, mapping text will be updated
                self.upload_file(item_id)

        if unsupported_names:
            msg = (
                "These files have extensions that are not supported for "
                "'assistants' uploads and will not be uploaded:\n\n"
                + "\n".join(f"- {name}" for name in unsupported_names)
            )
            messagebox.showwarning("Unsupported File Types", msg)



    def upload_file(self, item_id: str) -> None:
        record = self.file_records.get(item_id)
        if record is None:
            return
        if record.status in ("uploading", "uploaded", "unsupported"):
            return
        record.status = "uploading"
        self.update_tree_row(item_id)

        def worker() -> None:
            try:
                with open(record.local_path, "rb") as f:
                    uploaded = self.client.files.create(file=f, purpose="assistants")
                file_id = uploaded.id

                def on_success() -> None:
                    record.openai_file_id = file_id
                    record.status = "uploaded"
                    record.last_error = None
                    self.update_tree_row(item_id)
                    self.log(f"Uploaded {record.filename} -> {file_id}")
                    self._refresh_auto_instructions_preview()

                self.root.after(0, on_success)
            except Exception as e:  # noqa: BLE001
                error_text = str(e)

                def on_error() -> None:
                    invalid_ext = (
                        "Invalid extension" in error_text
                        and "Supported formats:" in error_text
                    )
                    if invalid_ext:
                        self._update_supported_extensions_from_error(error_text)
                        record.status = "unsupported"
                    else:
                        record.status = "error"

                    record.last_error = error_text
                    self.update_tree_row(item_id)
                    self.log(f"Error uploading {record.filename}:\n{error_text}")
                    # Avoid dialog storms for extension issues; only pop for other errors
                    if not invalid_ext:
                        messagebox.showerror("File Upload Error", error_text)
                    self._refresh_auto_instructions_preview()

                self.root.after(0, on_error)

        threading.Thread(target=worker, daemon=True).start()

    def update_tree_row(self, item_id: str) -> None:
        record = self.file_records.get(item_id)
        if record is None:
            return
        attached = "Yes" if record.attached_current else "No"
        assistants = ", ".join(sorted(record.attached_assistant_ids)) if record.attached_assistant_ids else ""
        self.tree.item(
            item_id,
            values=(
                record.filename,
                record.status,
                record.openai_file_id or "",
                attached,
                assistants,
            ),
        )

    def on_tree_double_click(self, event: tk.Event) -> None:  # type: ignore[override]
        item_id = self.tree.identify_row(event.y)
        if not item_id:
            return
        record = self.file_records.get(item_id)
        if record is None:
            return
        if record.status != "uploaded" or not record.openai_file_id:
            self.log(f"File not yet uploaded; cannot attach: {record.filename}")
            return
        record.attached_current = not record.attached_current
        self.update_tree_row(item_id)
        self._refresh_auto_instructions_preview()

    def toggle_attach_selected(self) -> None:
        selected = self.tree.selection()
        if not selected:
            return
        for item_id in selected:
            record = self.file_records.get(item_id)
            if record is None:
                continue
            if record.status != "uploaded" or not record.openai_file_id:
                self.log(
                    f"Cannot attach file until it has been uploaded successfully: {record.filename}"
                )
                continue
            record.attached_current = not record.attached_current
            self.update_tree_row(item_id)
        self._refresh_auto_instructions_preview()


    def delete_selected_files_from_server(self) -> None:
        selected = self.tree.selection()
        if not selected:
            return

        for item_id in list(selected):
            record = self.file_records.get(item_id)
            if record is None:
                continue

            if not record.openai_file_id:
                messagebox.showinfo(
                    "No Remote File",
                    f"\"{record.filename}\" has not been uploaded to the server.\n"
                    "Use 'Remove From List' to remove it from this table.",
                )
                continue

            if record.attached_assistant_ids:
                ids_str = ", ".join(sorted(record.attached_assistant_ids))
                msg = (
                    f"This file is attached to assistant(s): {ids_str}.\n"
                    "Deleting it will remove it for those assistants as well.\n\n"
                    "Are you sure you want to delete it from the server?"
                )
            else:
                msg = (
                    f"Delete remote file {record.openai_file_id} for {record.filename}?\n\n"
                    "This cannot be undone."
                )

            if not messagebox.askyesno("Delete File", msg):
                continue

            self._delete_remote_file(item_id, record)

    def _delete_remote_file(self, item_id: str, record: FileRecord) -> None:
        file_id = record.openai_file_id
        if not file_id:
            self._remove_file_record(item_id, record)
            return

        def worker() -> None:
            try:
                self.client.files.delete(file_id)

                def on_success() -> None:
                    self.log(f"Deleted remote file {file_id} ({record.filename}).")
                    record.status = "deleted"
                    self._remove_file_record(item_id, record)

                self.root.after(0, on_success)
            except Exception as e:  # noqa: BLE001
                error_text = str(e)

                def on_error() -> None:
                    self.log(
                        f"Error deleting remote file {file_id} ({record.filename}):\n{error_text}"
                    )
                    messagebox.showerror("Delete File Error", error_text)

                self.root.after(0, on_error)

        threading.Thread(target=worker, daemon=True).start()

    def remove_selected_from_list(self) -> None:
        selected = self.tree.selection()
        if not selected:
            return

        for item_id in list(selected):
            record = self.file_records.get(item_id)
            if record is None:
                continue

            self.log(
                f"Removed from list (remote file preserved if it was uploaded): "
                f"{record.filename}"
            )
            self._remove_file_record(item_id, record)

    def _remove_file_record(self, item_id: str, record: FileRecord) -> None:
        self.tree.delete(item_id)
        self.file_records.pop(item_id, None)
        self.path_index.pop(record.local_path, None)
        self._refresh_auto_instructions_preview()

    def _parse_assistant_fields(self) -> tuple[str | None, str, str, float, float, dict]:
        name = self.name_var.get().strip() or None
        model = self.model_var.get().strip() or "gpt-4.1"
        temp_str = self.temperature_var.get().strip()
        top_p_str = self.top_p_var.get().strip()

        try:
            temperature = float(temp_str) if temp_str else 0.1
        except ValueError:
            temperature = 0.1
            self.temperature_var.set(str(temperature))
            self.log("Invalid temperature value; reset to 0.1.")

        try:
            top_p = float(top_p_str) if top_p_str else 1.0
        except ValueError:
            top_p = 1.0
            self.top_p_var.set(str(top_p))
            self.log("Invalid top_p value; reset to 1.0.")

        import json

        metadata_text = self.metadata_text.get("1.0", "end").strip()
        metadata: dict = {}
        if metadata_text:
            try:
                parsed = json.loads(metadata_text)
                if not isinstance(parsed, dict):
                    raise ValueError("Metadata JSON must be an object (mapping).")
                metadata = parsed
            except Exception as e:  # noqa: BLE001
                messagebox.showerror("Metadata Error", f"Could not parse metadata JSON:\n{e}")
                raise

        instructions = self.instructions_text.get("1.0", "end").strip()
        return name, instructions, model, temperature, top_p, metadata

    def _collect_attached_file_ids(self) -> set[str]:
        file_ids: set[str] = set()
        for record in self.file_records.values():
            if record.attached_current and record.status == "uploaded" and record.openai_file_id:
                file_ids.add(record.openai_file_id)
        return file_ids

    def _build_file_mapping_instructions(self, file_ids: set[str]) -> str:
        if not file_ids:
            return ""

        rows: list[tuple[str, str]] = []
        for record in self.file_records.values():
            if record.openai_file_id and record.openai_file_id in file_ids:
                rows.append((record.openai_file_id, record.filename))

        if not rows:
            return ""

        rows.sort(key=lambda pair: pair[1].lower())

        parts: list[str] = []
        parts.append(
            "\n\n---\n\n"
            "These file names have been initially uploaded to your mount point at `/mnt/data`, "
            "but have been uploaded with an ID in the format `file-xxx`. "
            "Here is a map to the original file name for your understanding:\n\n"
        )
        parts.append("| uploaded file ID | original file name |\n")
        parts.append("| --- | --- |\n")
        for file_id, filename in rows:
            parts.append(f"| `{file_id}` | `{filename}` |\n")

        return "".join(parts)

    def _refresh_auto_instructions_preview(self) -> None:
        if not hasattr(self, "auto_instructions_text"):
            return

        if not self.code_interpreter_var.get():
            mapping_text = ""
        else:
            file_ids = self._collect_attached_file_ids()
            mapping_text = self._build_file_mapping_instructions(file_ids)

        self.auto_instructions_text.configure(state="normal")
        self.auto_instructions_text.delete("1.0", "end")
        if mapping_text:
            # Show only the appended section; strip leading blank lines for readability
            self.auto_instructions_text.insert("1.0", mapping_text.lstrip())
        self.auto_instructions_text.configure(state="disabled")

    def create_or_update_assistant(self) -> None:
        try:
            name, instructions, model, temperature, top_p, metadata = self._parse_assistant_fields()
        except Exception:
            return

        tools: list[dict] = []
        tool_resources: dict = {}

        if self.code_interpreter_var.get():
            tools.append({"type": "code_interpreter"})
            file_ids_set = self._collect_attached_file_ids()
            file_ids = list(file_ids_set)
            if file_ids:
                tool_resources["code_interpreter"] = {"file_ids": file_ids}
        else:
            file_ids_set = set()
            file_ids = []

        mapping_suffix = ""
        if file_ids_set:
            mapping_suffix = self._build_file_mapping_instructions(file_ids_set)

        # Keep preview in sync with whatever we'll actually send
        self._refresh_auto_instructions_preview()

        full_instructions = instructions + mapping_suffix if mapping_suffix else instructions

        if not full_instructions:
            if not messagebox.askyesno(
                "No Instructions",
                "No instructions text has been provided. Continue anyway?",
            ):
                return

        kwargs: dict = {
            "instructions": full_instructions or None,
            "model": model,
            "tools": tools,
            "temperature": temperature,
            "top_p": top_p,
        }
        if name:
            kwargs["name"] = name
        if metadata:
            kwargs["metadata"] = metadata
        if tool_resources:
            kwargs["tool_resources"] = tool_resources

        is_create = self.current_assistant_id is None
        if is_create:
            action_label = "create"
        else:
            action_label = "update"

        self.log(
            f"Submitting assistant {action_label} request with model={model}, "
            f"{len(file_ids)} attached file(s)."
        )

        def worker() -> None:
            try:
                if is_create:
                    assistant = self.client.beta.assistants.create(**kwargs)
                else:
                    assistant = self.client.beta.assistants.update(
                        self.current_assistant_id,
                        **kwargs,
                    )

                def on_success() -> None:
                    self.current_assistant_id = assistant.id
                    self.assistant_id_var.set(assistant.id)
                    self.create_update_button.configure(text="Update Assistant")

                    returned_file_ids: set[str] = set()
                    tool_resources_obj = getattr(assistant, "tool_resources", None)
                    if tool_resources_obj is not None:
                        code_interp = getattr(tool_resources_obj, "code_interpreter", None)
                        if code_interp is not None:
                            returned_ids = getattr(code_interp, "file_ids", None)
                            if returned_ids:
                                returned_file_ids = set(returned_ids)

                    missing = file_ids_set - returned_file_ids
                    if missing:
                        self.log(
                            "Warning: some file IDs were not present in the assistant's "
                            f"code interpreter tool resources: {', '.join(sorted(missing))}"
                        )
                    else:
                        self.log(
                            f"Assistant {assistant.id} {action_label}d successfully "
                            f"with {len(returned_file_ids)} attached file(s)."
                        )

                    for item_id, record in self.file_records.items():
                        if record.openai_file_id and record.openai_file_id in returned_file_ids:
                            record.attached_assistant_ids.add(assistant.id)
                            record.attached_current = True
                        elif record.attached_current:
                            record.attached_current = False
                        self.update_tree_row(item_id)

                    self._refresh_auto_instructions_preview()

                self.root.after(0, on_success)
            except Exception as e:  # noqa: BLE001
                error_text = str(e)

                def on_error() -> None:
                    self.log(
                        f"Error during assistant {action_label}:\n{error_text}"
                    )
                    messagebox.showerror(
                        "Assistant Error",
                        f"Assistant {action_label} failed:\n{error_text}",
                    )

                self.root.after(0, on_error)

        threading.Thread(target=worker, daemon=True).start()


def main() -> None:
    root = tk.Tk()
    root.geometry("1400x900")
    app = AssistantBuilderApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()

You’ll see it does a function that is almost mandatory for Assistants and code interpreter to actually “work”, when the mount point only gets IDs, and the AI has no knowledge of what’s available without listing with code - it provides the original file names to the AI.