juicedata/juicefs · error · ValueError

must have exactly one of create/read/write/append mode

Error message

must have exactly one of create/read/write/append mode

What it means

After scanning the mode characters, open() requires exactly one base mode: r, w, x or a. cnt != 1 (zero base modes like '+' or 'tb' alone, or multiple like 'rwa') raises this ValueError. Mirrors CPython's builtin open() error message.

Source

Thrown at sdk/python/juicefs/juicefs/juicefs.py:220

    def open(self, path, mode='r', buffering=-1, encoding=None, errors=None):
        """Open a file, returns a filelike object."""
        if len(mode) != len(set(mode)):
            raise ValueError(f'invalid mode: {mode}')
        flag = 0
        cnt = 0
        for c in mode:
            if c in 'rwxa':
                cnt += 1
                if c == 'r':
                    flag |= MODE_READ
                else:
                    flag |= MODE_WRITE
            elif c == '+':
                flag |= MODE_READ | MODE_WRITE
            elif c not in 'tb':
                raise ValueError(f'invalid mode: {mode}')
        if cnt != 1:
            raise ValueError('must have exactly one of create/read/write/append mode')
        if 'b' in mode:
            if 't' in mode:
                raise ValueError("can't have text and binary mode at once")
            if encoding:
                raise ValueError("binary mode doesn't take an encoding argument")
            if errors:
                raise ValueError("binary mode doesn't take an errors argument")
        else:
            if not encoding:
                encoding = locale.getpreferredencoding(False).lower()
            if not errors:
                errors = 'strict'
            codecs.lookup(encoding)

        size = 0
        if 'x' in mode:
            fd = self.lib.jfs_create(c_int64(_tid()), c_int64(self.h), _bin(path), c_uint16(0o666), c_uint16(self.umask))
        else:

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Set exactly one of 'r', 'w', 'x' or 'a' in the mode string.
  2. For read+write use 'r+' or 'w+' rather than 'rw'.
  3. Validate config-supplied modes against ^(r|w|x|a)[+]?[tb]?$ before calling open.

Example fix

# before
f = jfs.open(path, 'rw')   # two base modes
# after
f = jfs.open(path, 'r+')   # exactly one base mode
Defensive patterns

Strategy: validation

Validate before calling

import re
if not re.fullmatch(r'(r|w|x|a)\+?[tb]?', mode):
    raise ValueError(f'mode must have exactly one of r/w/x/a: {mode}')

Type guard

def has_exactly_one_base_mode(mode) -> bool:
    return isinstance(mode, str) and sum(c in 'rwxa' for c in mode) == 1

Try / catch

try:
    f = jfs.open(path, mode)
except ValueError as e:
    if 'exactly one of create/read/write/append' in str(e):
        f = jfs.open(path, mode + 'r' if sum(c in 'rwxa' for c in mode) == 0 else 'r+')
    else:
        raise

Prevention

When it happens

Trigger: open(path, '+'), open(path, 'b'), open(path, 't'), open(path, 'rwa'), open(path, '') — zero or 2+ base-mode characters.

Common situations: Constructing the mode dynamically and ending up with only '+' or 'b'; concatenating 'a' onto 'rw' making 'rwa'; empty mode string from a config value defaulting to ''.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/ff4a03187bd6f02c. Report an issue: GitHub.