pallets/flask · error · ValueError

When specifying '_scheme', '_external' must be True.

Error message

When specifying '_scheme', '_external' must be True.

What it means

Raised in `Flask.url_for()` (src/flask/app.py:1200) when `_scheme` is passed together with an explicit `_external=False`. A scheme (e.g. 'https') only appears in absolute URLs; a relative URL cannot carry one, and Flask rejects the combination explicitly to avoid silently dropping the scheme and producing accidentally insecure links.

Source

Thrown at src/flask/app.py:1200

                url_adapter = self.create_url_adapter(None)

            if url_adapter is None:
                raise RuntimeError(
                    "Unable to build URLs outside an active request"
                    " without 'SERVER_NAME' configured. Also configure"
                    " 'APPLICATION_ROOT' and 'PREFERRED_URL_SCHEME' as"
                    " needed."
                )

            # When outside a request, generate a URL with scheme and
            # domain by default.
            if _external is None:
                _external = True

        # It is an error to set _scheme when _external=False, in order
        # to avoid accidental insecure URLs.
        if _scheme is not None and not _external:
            raise ValueError("When specifying '_scheme', '_external' must be True.")

        self.inject_url_defaults(endpoint, values)

        try:
            rv = url_adapter.build(  # type: ignore[union-attr]
                endpoint,
                values,
                method=_method,
                url_scheme=_scheme,
                force_external=_external,
            )
        except BuildError as error:
            values.update(
                _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external
            )
            return self.handle_url_build_error(error, endpoint, values)

        if _anchor is not None:

View on GitHub ↗ (pinned to 6a2f545bfd)

Solutions

  1. Remove `_external=False` — passing `_scheme` implies you want an absolute URL, and Flask will set `_external=True` for you.
  2. If you actually want a relative URL, drop the `_scheme` argument instead.
  3. To make all generated URLs https without per-call arguments, set `PREFERRED_URL_SCHEME = 'https'` in config (used outside requests) or fix your proxy headers (`X-Forwarded-Proto` with ProxyFix) so in-request URLs get the right scheme.

Example fix

# before
url = url_for("login", _scheme="https", _external=False)

# after
url = url_for("login", _scheme="https", _external=True)
# or, for a relative URL:
url = url_for("login")
Defensive patterns

Strategy: validation

Validate before calling

kwargs = {'_scheme': 'https'}
if '_scheme' in kwargs:
    kwargs['_external'] = True
url = url_for('index', **kwargs)

Prevention

When it happens

Trigger: `url_for('endpoint', _scheme='https', _external=False)`. Inside a request, passing `_scheme` alone auto-sets `_external=True` (app.py:1173-1174), so the error only fires when `_external=False` is passed explicitly alongside `_scheme`.

Common situations: Copy-pasted url_for calls where someone added `_external=False` to 'fix' full URLs appearing in links while a global helper still passes `_scheme='https'`; wrapper functions that forward both kwargs with conflicting defaults; code written for older Flask versions where `_scheme` behavior differed (pre-2.2 required `_external=True` explicitly with `_scheme`).

Related errors


AI-assisted analysis of pallets/flask@6a2f545bfd (2026-07-31). Data as JSON: /data/errors/8d06427abfec2385.json. Report an issue: GitHub ↗.