Skip to content

files.move_to / copy_to 500 on paths with spaces or reserved characters (raw path sent in JSON body) #124

Description

@andrii-novikov

Name and Version

aidial-client 0.14.0 (also reproduces on 0.12.0)

What steps will reproduce the bug?

  1. Have a DIAL file whose name contains a space (or any URL-reserved character), e.g. files/<bucket>/my file.txt.
  2. Call the move (or copy) operation:
from aidial_client import AsyncDial

client = AsyncDial(...)
await client.files.move_to(
    source="files/<bucket>/my file.txt",
    destination="files/<bucket>/renamed file.txt",
    overwrite=False,
)
  1. DIAL Core responds with 500.

Root cause. move_to / copy_to place the resolved path into the JSON body verbatim, whereas every other file operation places it into the request URL:

# move_to / copy_to (resources/files.py) — path goes into the body
json_data={
    "sourceUrl": self.get_api_path(str(source)),         # 'files/<bucket>/my file.txt'  ← raw space
    "destinationUrl": self.get_api_path(str(destination)),
    "overwrite": overwrite,
}

# upload / download / delete — path goes into the URL
url=urljoin(API_PREFIX, self.get_api_path(str(url)))

get_api_path returns the path unencoded (a literal space is preserved). For upload/download/delete that is harmless because httpx percent-encodes the URL path on the wire (my file.txtmy%20file.txt), so DIAL Core receives a valid resource URL. For move_to/copy_to the value sits in a JSON string, where nothing encodes it, so DIAL Core's ops/resource/move and ops/resource/copy endpoints receive a raw space in a field they parse as a percent-encoded resource URL, and 500.

Verification:

>>> from aidial_client.helpers.storage_resource import safe_parse_storage_resource
>>> safe_parse_storage_resource(url='files/mybucket/my file.txt', dial_api_url='http://core/v1/').api_path
'files/mybucket/my file.txt'   # literal space kept — fine in a URL slot, broken in a body slot

Any URL-reserved character in a filename triggers this (space, #, ?, %, …), not only spaces.

What is the expected behavior?

move_to / copy_to succeed for any valid filename, consistent with upload / download / delete. The sourceUrl / destinationUrl values sent in the JSON body should be percent-encoded resource URLs (e.g. files/<bucket>/my%20file.txt), matching what DIAL Core receives for the other operations.

What do you see instead?

A 500 from DIAL Core whenever the source or destination path contains a space or other URL-reserved character. Filenames without reserved characters work correctly.

Additional information

The other file operations avoid the problem only incidentally, because httpx encodes the URL path — the body path is never encoded. A fix should percent-encode the path when building the sourceUrl / destinationUrl body fields for both the sync and async move_to / copy_to, preserving / separators (e.g. urllib.parse.quote(path, safe="/")).

Affected: both Files (sync) and AsyncFiles in aidial_client/resources/files.py.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions