iOfficeAI/OfficeCLI · warning · JsonException

Unexpected token {reader.TokenType} for prop value '{key}'

Error message

Unexpected token {reader.TokenType} for prop value '{key}'

What it means

JsonException from LenientStringDictionaryConverter.Read inside the OBJECT branch: the value for a props key is a token type the converter won't coerce — specifically a nested object or array (StartObject/StartArray), since strings/numbers/booleans/null are all accepted and stringified. The message names the offending key and token type.

Source

Thrown at src/officecli/BatchTypes.cs:51

            throw new JsonException("Unexpected end of JSON");
        }
        if (reader.TokenType != JsonTokenType.StartObject)
            throw new JsonException("Expected object or [\"key=value\"] array for props");
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject) return dict;
            if (reader.TokenType != JsonTokenType.PropertyName)
                throw new JsonException("Expected property name");
            var key = reader.GetString()!;
            reader.Read();
            var value = reader.TokenType switch
            {
                JsonTokenType.String => reader.GetString()!,
                JsonTokenType.Number => reader.TryGetInt64(out var l) ? l.ToString() : reader.GetDouble().ToString(),
                JsonTokenType.True => "true",
                JsonTokenType.False => "false",
                JsonTokenType.Null => "",
                _ => throw new JsonException($"Unexpected token {reader.TokenType} for prop value '{key}'")
            };
            dict[key] = value;
        }
        throw new JsonException("Unexpected end of JSON");
    }

    public override void Write(Utf8JsonWriter writer, Dictionary<string, string> value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        foreach (var kv in value)
            writer.WriteString(kv.Key, kv.Value);
        writer.WriteEndObject();
    }
}

internal class BatchItemConverter : JsonConverter<BatchItem>
{
    private static readonly LenientStringDictionaryConverter PropsConverter = new();

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Flatten the value to a string, e.g. props: { style: 'bold' } or a serialized form the command accepts.
  2. Use the command-specific arguments officecli supports rather than nesting under props.
  3. Confirm against the command's schema which props are scalar string-valued.

Example fix

// before: { command:'set', props: { style: { bold: true, size: 14 } } }
// after:  { command:'set', props: { bold: 'true', size: '14' } }  // flat string map
Defensive patterns

Strategy: type-guard

Validate before calling

// Flatten props to a string map before serializing
function flattenProps(p) {
  const out = {};
  for (const [k, v] of Object.entries(p || {})) out[k] = (typeof v === 'object') ? JSON.stringify(v) : String(v);
  return out;
}

Type guard

// props values must be scalar (string|number|boolean), not nested objects/arrays
function isFlatProps(p) {
  if (p == null) return true;
  return Object.values(p).every(v => v == null || ['string','number','boolean'].includes(typeof v));
}

Prevention

When it happens

Trigger: "props": { "style": {"bold":true} } (a nested object), or "props": { "vals": [1,2,3] } (a nested array). officecli props are flat string maps; nested structures are not supported.

Common situations: An agent that wants to set a rich/structured cell style but passes a nested object; reusing a config object verbatim as props.

Understand the failure class

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/8ae75cf7abcdbfed. Report an issue: GitHub.