XINCGer/Unity3DTraining · error · InvalidOperationException

Invalid field mask to be converted to JSON

Error message

Invalid field mask to be converted to JSON: {firstInvalid}

What it means

FieldMask.ToJson validates that every path in the mask is a valid lowercase snake_case field path (validating conversion). If any path contains characters invalid for JSON field-mask representation (e.g. CamelCase segments that would need camel-conversion but are invalid), it throws InvalidOperationException naming the first invalid path. Field masks in JSON must use camelCase, and the library requires paths it can validate before converting.

Solutions

  1. Convert paths to valid snake_case protobuf field names before adding them (FieldMask.Util.ToUpperCamelCase is for output; input must be lowercase snake_case)
  2. Use FieldMask.ForFieldNames or construct from reflection-derived protobuf field names rather than raw strings
  3. Fix the specific path named in the exception message
  4. If interop requires camelCase input, normalize with a snake_case converter before adding to Paths

Example fix

// before
var mask = new FieldMask();
mask.Paths.Add("DisplayName"); // invalid
var s = mask.ToDiagnosticString(); // throws
// after
var mask2 = new FieldMask();
mask2.Paths.Add("display_name");
var s2 = mask2.ToDiagnosticString(); // "displayName"
Defensive patterns

Strategy: validation

Validate before calling

static bool IsValidFieldMaskPath(string p) => !string.IsNullOrEmpty(p) && p.All(c => char.IsLower(c) || char.IsDigit(c) || c == '_' || c == '.');

Type guard

static bool IsUsableFieldMask(FieldMask m) => m != null && m.Paths.Count > 0 && m.Paths.All(IsValidFieldMaskPath);

Try / catch

try { return mask.ToDiagnosticString(); } catch (InvalidOperationException ex) { log.Warn(ex, "Invalid field mask"); return string.Join(",", mask.Paths); }

Prevention

When it happens

Trigger: Calling ToDiagnosticString()/ToJson() on a FieldMask whose Paths contain entries like 'FooBar' or paths with invalid characters, so IsValidPath/validating conversion fails and firstInvalid holds the offending path.

Common situations: Building field masks from C# property names (PascalCase) instead of protobuf field names; hand-writing paths with typos; masks constructed programmatically from user input; mixing camelCase and snake_case when copying from JSON examples.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12). Data as JSON: /api/errors/b9c786da530ac5c3. Report an issue: GitHub.

Appendix: source

Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/WellKnownTypes/FieldMaskPartial.cs:78

            {
                var writer = new StringWriter();
                var query = paths.Select<string, string>(JsonFormatter.ToJsonName);
                JsonFormatter.WriteString(writer, string.Join(",", query.ToArray()));
                return writer.ToString();
            }
            else
            {
                if (diagnosticOnly)
                {
                    var writer = new StringWriter();
                    writer.Write("{ \"@warning\": \"Invalid FieldMask\", \"paths\": ");
                    JsonFormatter.Default.WriteList(writer, (IList)paths);
                    writer.Write(" }");
                    return writer.ToString();
                }
                else
                {
                    throw new InvalidOperationException("Invalid field mask to be converted to JSON: " + firstInvalid);
                }
            }
        }

        /// <summary>
        /// Checks whether the given path is valid for a field mask.
        /// </summary>
        /// <returns>true if the path is valid; false otherwise</returns>
        private static bool ValidatePath(string input)
        {
            for (int i = 0; i < input.Length; i++)
            {
                char c = input[i];
                if (c >= 'A' && c <= 'Z')
                {
                    return false;
                }
                if (c == '_' && i < input.Length - 1)

View on GitHub (pinned to 016f98412e)