# Buggy code
def _replace_match(self, match):
    g = match.groupdict()
    sub_value = g["substitution_value"]
    if self.crossonly:
        if sub_value.startswith("["):
            return self._substitute_from_other_section(sub_value)
        # in crossonly we return all other hits verbatim
        start, end = match.span()
        return match.string[start:end]

    full_match = match.group(0)
    # ":" is swallowed by the regex, so the raw matched string is checked
    if full_match.startswith("{:"):
        if full_match != "{:}":
            raise tox.exception.ConfigError(
                "Malformed substitution with prefix ':': {}".format(full_match),
            )

        return os.pathsep

    default_value = g["default_value"]
    if sub_value == "posargs":
        return self.reader.getposargs(default_value)

    sub_type = g["sub_type"]
    if sub_type == "posargs":
        if default_value:
            value = "{}:{}".format(sub_value, default_value)
        else:
            value = sub_value
        return self.reader.getposargs(value)

    if not sub_type and not sub_value:
        raise tox.exception.ConfigError(
            "Malformed substitution; no substitution type provided. "
            "If you were using `{}` for `os.pathsep`, please use `{:}`.",
        )

    if not sub_type and not default_value and sub_value == "/":
        return os.sep

    if sub_type == "env":
        return self._replace_env(sub_value, default_value)
    if sub_type == "tty":
        if is_interactive():
            return match.group("substitution_value")
        return match.group("default_value")
    if sub_type == "posargs":
        return self.reader.getposargs(sub_value)
    if sub_type is not None:
        raise tox.exception.ConfigError(
            "No support for the {} substitution type".format(sub_type),
        )
    return self._replace_substitution(sub_value)


# Traceback (tox.exception.ConfigError)
$ tox -rvv
using tox.ini: /tmp/test/tox.ini (pid 8575)
removing /tmp/test/.tox/log
Traceback (most recent call last):
File "/tmp/venv/bin/tox", line 8, in <module>
sys.exit(cmdline())
File "/tmp/venv/lib/python3.8/site-packages/tox/session/__init__.py", line 44, in cmdline
main(args)
File "/tmp/venv/lib/python3.8/site-packages/tox/session/__init__.py", line 65, in main
config = load_config(args)
File "/tmp/venv/lib/python3.8/site-packages/tox/session/__init__.py", line 81, in load_config
config = parseconfig(args)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 280, in parseconfig
ParseIni(config, config_file, content)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1276, in __init__
raise tox.exception.ConfigError(
tox.exception.ConfigError: ConfigError: py38 failed with ConfigError: substitution key 'opts' not found at Traceback (most recent call last):
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1252, in run
results[name] = cur_self.make_envconfig(name, section, subs, config)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1407, in make_envconfig
res = meth(env_attr.name, env_attr.default, replace=replace)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1716, in getargv_install_command
return _ArgvlistReader.getargvlist(self, s, replace=replace, name=name)[0]
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1946, in getargvlist
commands.extend(cls.getargvlist(reader, replaced, name=name))
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1948, in getargvlist
commands.append(cls.processcommand(reader, current_command, replace, name=name))
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1974, in processcommand
new_word = reader._replace(word, name=name)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1782, in _replace
replaced = Replacer(self, crossonly=crossonly).do_replace(value)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1818, in do_replace
expanded = substitute_once(value)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1816, in substitute_once
return self.RE_ITEM_REF.sub(self._replace_match, x)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1876, in _replace_match
return self._replace_substitution(sub_value)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1911, in _replace_substitution
val = self._substitute_from_other_section(sub_key)
File "/tmp/venv/lib/python3.8/site-packages/tox/config/__init__.py", line 1906, in _substitute_from_other_section
raise tox.exception.ConfigError("substitution key {!r} not found".format(key))
tox.exception.ConfigError: ConfigError: substitution key 'opts' not found

# Fixed code
def _replace_match(self, match):
    g = match.groupdict()
    sub_value = g["substitution_value"]
    if self.crossonly:
        if sub_value.startswith("["):
            return self._substitute_from_other_section(sub_value)
        # in crossonly we return all other hits verbatim
        start, end = match.span()
        return match.string[start:end]

    full_match = match.group(0)
    # ":" is swallowed by the regex, so the raw matched string is checked
    if full_match.startswith("{:"):
        if full_match != "{:}":
            raise tox.exception.ConfigError(
                "Malformed substitution with prefix ':': {}".format(full_match),
            )

        return os.pathsep

    default_value = g["default_value"]
    # special case: opts and packages. Leave {opts} and
    # {packages} intact, they are replaced manually in
    # _venv.VirtualEnv.run_install_command.
    if sub_value in ("opts", "packages"):
        return "{{{}}}".format(sub_value)

    if sub_value == "posargs":
        return self.reader.getposargs(default_value)

    sub_type = g["sub_type"]
    if sub_type == "posargs":
        if default_value:
            value = "{}:{}".format(sub_value, default_value)
        else:
            value = sub_value
        return self.reader.getposargs(value)

    if not sub_type and not sub_value:
        raise tox.exception.ConfigError(
            "Malformed substitution; no substitution type provided. "
            "If you were using `{}` for `os.pathsep`, please use `{:}`.",
        )

    if not sub_type and not default_value and sub_value == "/":
        return os.sep

    if sub_type == "env":
        return self._replace_env(sub_value, default_value)
    if sub_type == "tty":
        if is_interactive():
            return match.group("substitution_value")
        return match.group("default_value")
    if sub_type == "posargs":
        return self.reader.getposargs(sub_value)
    if sub_type is not None:
        raise tox.exception.ConfigError(
            "No support for the {} substitution type".format(sub_type),
        )
    return self._replace_substitution(sub_value)