{"record":{"id":"b255d042fd57e762","repo":"microsoft/aspire","slug":"aspire-hosting-dict-toobject-only-supports-string-key","errorCode":null,"errorMessage":"Aspire.Hosting/Dict.toObject only supports string-key dictionaries, but found key type '{key?.GetType().FullName ?? \"null\"}'.","messagePattern":"Aspire\\.Hosting/Dict\\.toObject only supports string-key dictionaries, but found key type '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Ats/CollectionExports.cs","lineNumber":202,"sourceCode":"    private static Type? GetDictionaryKeyType(Type type)\n    {\n        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))\n        {\n            return type.GetGenericArguments()[0];\n        }\n\n        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))\n        {\n            return type.GetGenericArguments()[0];\n        }\n\n        return type.GetInterfaces()\n            .FirstOrDefault(static iface => iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IDictionary<,>))\n            ?.GetGenericArguments()[0];\n    }\n\n    private static string GetStringKey(object? key)\n        => key as string ?? throw new InvalidOperationException($\"Aspire.Hosting/Dict.toObject only supports string-key dictionaries, but found key type '{key?.GetType().FullName ?? \"null\"}'.\");\n\n    #endregion\n\n    #region List Operations\n\n    /// <summary>\n    /// Gets an item from a list by index.\n    /// </summary>\n    /// <param name=\"list\">The list handle.</param>\n    /// <param name=\"index\">The zero-based index.</param>\n    /// <returns>The item at the specified index.</returns>\n    [AspireExport(\"List.get\")]\n    public static object? ListGet(this IList list, int index)\n        => index >= 0 && index < list.Count ? list[index] : null;\n\n    /// <summary>\n    /// Sets an item in a list at a specific index.\n    /// </summary>","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Ats/CollectionExports.cs#L184-L220","documentation":"Dict.toObject in the Aspire.Hosting ATS exports only supports dictionaries whose keys are strings; GetStringKey casts the key via `key as string` and throws InvalidOperationException when a non-string (or null) key is encountered. This guard exists because the scripting layer cannot represent non-string dictionary keys.","triggerScenarios":"Calling Dict.toObject on an IDictionary<TKey,TValue> where keys are not strings — e.g., Dictionary<int, ...>, Dictionary<Guid, ...>, Dictionary<MyEnum, ...> — or a dictionary containing a null key.","commonSituations":"Passing a numerically-keyed dictionary (config lookups keyed by int, cache keyed by Guid or enum) into the ATS Dict export; localization or lookup maps keyed by enum values.","solutions":["Convert the dictionary to Dictionary<string, TValue> (e.g., with ToDictionary(k => k.Key.ToString()!, ...)) before calling toObject.","If keys are enums, use the enum name or ToString as the key.","If keys are complex objects, replace them with a string identifier property."],"exampleFix":"// before\nDict.toObject(myIntKeyedDict);\n// after\nDict.toObject(myIntKeyedDict.ToDictionary(kv => kv.Key.ToString(), kv => kv.Value));","handlingStrategy":"type-guard","validationCode":"if (dict is not IDictionary<string, object> stringKeyed)\n{\n    throw new InvalidOperationException($\"Dict.toObject requires string keys; got {dict?.GetType().FullName ?? \"null\"}.\");\n}","typeGuard":"static bool HasStringKeys<TValue>(Dictionary<TKey, TValue> d) where TKey : notnull => typeof(TKey) == typeof(string);","tryCatchPattern":"try\n{\n    Dict.toObject(dict);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"only supports string-key\"))\n{\n    // fall back to projecting keys with ToString()\n}","preventionTips":["Prefer Dictionary<string, T> at model boundaries that feed the ATS exports.","Convert int/Guid/enum keys to strings when building dictionaries destined for scripting."],"tags":["dictionaries","type-mismatch","ats"],"backgroundTag":"type-mismatch","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}