Skip to content

[Bug]: VikingFS.mkdir() 从不创建目标目录,方法为空操作 #90

@ze-mu-zhou

Description

@ze-mu-zhou

Bug Description

VikingFS.mkdir() (openviking/storage/viking_fs.py lines 141–153) calls _ensure_parent_dirs(path) to create parent directories, but never calls self.agfs.mkdir(path) to create the target directory itself. The method returns silently — every mkdir() call is effectively a no-op.

async def mkdir(self, uri: str, mode: str = "755", exist_ok: bool = False) -> None:
    path = self._uri_to_path(uri)
    await self._ensure_parent_dirs(path)  # Only creates parents

    if exist_ok:
        try:
            await self.stat(uri)
            return None  # Already exists, return
        except Exception:
            pass  # Does not exist, fall through
    return  # ====== target directory never created ======

_ensure_parent_dirs (lines 656–670) iterates range(1, len(parts)), creating components 0 through N-2 (all parents), but NOT the final component (the target directory).

Steps to Reproduce

import openviking as ov
import asyncio

client = ov.SyncOpenViking(path="./data")
client.initialize()

viking_fs = client.viking_fs
test_uri = "viking://resources/bug1_test_dir"

# Call mkdir
asyncio.run(viking_fs.mkdir(test_uri, exist_ok=False))
# Returns without error

# Verify directory exists
asyncio.run(viking_fs.stat(test_uri))
# Raises AGFSClientError: no such file or directory: /resources/bug1_test_dir

client.close()

Can also be verified via pure logic analysis:

path = "/local/resources/new_project"
parts = path.lstrip("/").split("/")
# parts = ["local", "resources", "new_project"]

for i in range(1, len(parts)):  # i = 1, 2
    parent = "/" + "/".join(parts[:i])
    print(f"agfs.mkdir({parent})")

# Output:
#   agfs.mkdir(/local)
#   agfs.mkdir(/local/resources)
# "/local/resources/new_project" is never created

Expected Behavior

await viking_fs.mkdir("viking://resources/new_project") should create the target directory /local/resources/new_project via self.agfs.mkdir(path).

Actual Behavior

The directory is never created. The method returns silently. Subsequent stat calls raise:

AGFSClientError: no such file or directory: /resources/bug1_test_dir

Verified on Windows + Python 3.13.7.

Minimal Reproducible Example

import openviking as ov
import asyncio

client = ov.SyncOpenViking(path="./data")
client.initialize()

viking_fs = client.viking_fs

asyncio.run(viking_fs.mkdir("viking://resources/test_dir", exist_ok=False))
# No error

asyncio.run(viking_fs.stat("viking://resources/test_dir"))
# AGFSClientError: no such file or directory: /resources/test_dir

client.close()

Error Logs

[2] Calling viking_fs.mkdir('viking://resources/bug1_test_dir', exist_ok=False)...
[2] mkdir returned without error

[3] Calling viking_fs.stat('viking://resources/bug1_test_dir') to verify...
[3] stat raised: AGFSClientError: no such file or directory: /resources/bug1_test_dir

Conclusion: BUG confirmed — mkdir() is a no-op, directory was never created

OpenViking Version

0.1.10 (main branch, 2026-02-07)

Python Version

3.13.7

Operating System

Windows

Model Backend

Other

Additional Context

The missing line should be await asyncio.to_thread(self.agfs.mkdir, path) before the final return.

This bug affects all code paths that rely on VikingFS.mkdir(), including:

  • TreeBuilder._move_directory_in_agfs (line 163)
  • DirectoryInitializer
  • import_ovpack

The system may appear to work in some cases only because write_file independently calls _ensure_parent_dirs, which creates intermediate directories as a side effect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions