pallets/flask · error · ValueError

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

Error message

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

What it means

Raised by Flask.url_for() when _scheme is set but _external is False. Forcing a scheme only makes sense for absolute (external) URLs; allowing scheme + internal would silently produce insecure/misleading URLs, so Flask rejects it explicitly.

Solutions

  1. Set _external=True when using _scheme: url_for('endpoint', _scheme='https', _external=True).
  2. Drop _scheme if you want a relative/internal URL.
  3. Configure PREFERRED_URL_SCHEME in app config for default scheme instead of per-call _scheme.

Example fix

// before
url_for('login', _scheme='https', _external=False)  # raises
// after
url_for('login', _scheme='https', _external=True)
Defensive patterns

Strategy: validation

Validate before calling

if scheme is not None and external is False:
    raise ValueError('_external must be True when _scheme is set')
url_for('endpoint', _scheme=scheme, _external=external)

Type guard

def scheme_external_compatible(scheme, external) -> bool:
    return scheme is None or external is True or external is None

Try / catch

try:
    url = url_for('endpoint', _scheme=scheme, _external=external)
except ValueError as e:
    if '_external' in str(e):
        external = True
        url = url_for('endpoint', _scheme=scheme, _external=external)
    raise

Prevention

When it happens

Trigger: Calling url_for('endpoint', _scheme='https', _external=False) — scheme present but external explicitly disabled.

Common situations: Copy-paste of url_for args where _external was set to False for a relative link while keeping a _scheme; partial migration of http->https link logic.

Related errors


AI-assisted analysis of pallets/flask@3596b1ab61 (2026-08-11). Data as JSON: /api/errors/8d06427abfec2385. Report an issue: GitHub.

Appendix: source

Thrown at src/flask/app.py:1203

                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 3596b1ab61)