nodejs/node · error · ValueError

expected string; got %r

Error message

expected string; got %r

What it means

_String.ValidateMSVS in MSVSSettings.py raises ValueError('expected string; got %r') when the value supplied for an MSVS setting whose declared type is _String is not a Python str. Gyp validates each msvs_settings entry against a type table; the _String type only accepts str, rejecting ints, bools, lists, or None. This guards the gyp -> .vcproj conversion from emitting malformed XML attributes.

Source

Thrown at tools/gyp/pylib/gyp/MSVSSettings.py:110

        Args:
          value: the MSVS value to convert.

        Returns:
          the MSBuild equivalent.

        Raises:
          ValueError if value is not valid.
        """
        return value


class _String(_Type):
    """A setting that's just a string."""

    def ValidateMSVS(self, value):
        if not isinstance(value, str):
            raise ValueError("expected string; got %r" % value)

    def ValidateMSBuild(self, value):
        if not isinstance(value, str):
            raise ValueError("expected string; got %r" % value)

    def ConvertToMSBuild(self, value):
        # Convert the macros
        return ConvertVCMacrosToMSBuild(value)


class _StringList(_Type):
    """A settings that's a list of strings."""

    def ValidateMSVS(self, value):
        if not isinstance(value, (list, str)):
            raise ValueError("expected string list; got %r" % value)

    def ValidateMSBuild(self, value):

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Inspect the named setting in the failing msvs_settings dict and coerce the value to str: str(value).
  2. Match the documented MSVS value format (e.g. Optimization expects "0"/"1"/"2"/"3"/"4" as strings, not ints).
  3. If the value is genuinely optional, omit the key instead of passing None.
  4. Run gyp with --debug general to see which target/setting triggered the validator.

Example fix

# before
'Optimization': 2,

# after
'Optimization': '2',
Defensive patterns

Strategy: type-guard

Validate before calling

def coerce_string_setting(value):
    if value is None:
        return None  # caller should omit key
    if not isinstance(value, str):
        raise TypeError('MSVS string setting expects str, got %r' % type(value))
    return value

Type guard

def is_msvs_string(value) -> bool:
    return isinstance(value, str)

Try / catch

try:
    _string.ValidateMSVS(my_value)
except ValueError:
    my_value = str(my_value)  # or omit the setting
    _string.ValidateMSVS(my_value)

Prevention

When it happens

Trigger: A gyp file sets an MSVS string setting to a non-string YAML/JSON-like value, e.g. 'Optimization': 2 (int) or 'AdditionalIncludeDirectories': None; the msvs_settings dict is assembled programmatically and a number slips through where the schema expects a string macro like "$(IntDir)".

Common situations: Migrating a .gyp from another generator (make/ninja) where ints were tolerated; copy-pasting MSBuild-style boolean True/False into an MSVS-only setting; downstream tooling that emits numeric optimization levels.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/fec26c90ba3a87ef. Report an issue: GitHub.