Fixed settings which configuration plugins could not manage (#1080)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-10-17 21:14:43 +02:00
1 parent 055cce80a0
commit 8642ce2d81
8 files changed
+260 -17

No files matched your search

@@ -12,7 +12,7 @@
}
<ConfigurationSelect OptionDescription="@T("Color theme")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="@T("Choose the color theme that best suits for you.")"/>
<ConfigurationOption OptionDescription="@T("Save energy?")" LabelOn="@T("Energy saving is enabled")" LabelOff="@T("Energy saving is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="@T("When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available.")"/>
<ConfigurationOption OptionDescription="@T("Save energy?")" LabelOn="@T("Energy saving is enabled")" LabelOff="@T("Energy saving is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="@T("When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.IsSavingEnergy, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Enable spellchecking?")" LabelOn="@T("Spellchecking is enabled")" LabelOff="@T("Spellchecking is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="@T("When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections.")"/>
<ConfigurationOption OptionDescription="@T("Show details when suspicious content was removed?")" LabelOn="@T("A dialog lists what was removed and explains the attack pattern")" LabelOff="@T("Only a short notification is shown")" State="@(() => this.SettingsManager.ConfigurationData.App.ShowPromptInjectionAlert)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.ShowPromptInjectionAlert = updatedState)" OptionHelp="@T("Shows a dialog listing the removed passages, together with an explanation and an external reference.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShowPromptInjectionAlert, out var meta) && meta.IsLocked"/>
<ConfigurationSlider T="int" OptionDescription="@T("Request timeout")" Min="@ExternalHttpClientTimeout.MIN_HTTP_CLIENT_TIMEOUT_SECONDS" Max="@ExternalHttpClientTimeout.MAX_HTTP_CLIENT_TIMEOUT_SECONDS" Step="60" Unit="@T("seconds")" Value="@(() => this.SettingsManager.ConfigurationData.App.HttpClientTimeoutSeconds)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.App.HttpClientTimeoutSeconds = updatedValue)" OptionHelp="@T("How long AI Studio waits for external HTTP requests, such as AI providers, embeddings, transcription, ERI data sources, and enterprise configuration downloads.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.HttpClientTimeoutSeconds, out var meta) && meta.IsLocked"/>
@@ -10,20 +10,20 @@
</TitleContent>
<DialogContent>
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<ConfigurationSelect OptionDescription="@T("Default target language")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesOptionalData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedTargetLanguage = value)"/>
<ConfigurationSelect OptionDescription="@T("Default target language")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesOptionalData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedTargetLanguage = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.PreselectedTargetLanguage, out var meta) && meta.IsLocked"/>
@if (this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedTargetLanguage is CommonLanguages.OTHER)
{
<ConfigurationText OptionDescription="@T("Default custom target language")" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedOtherLanguage)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedOtherLanguage = value)"/>
<ConfigurationText OptionDescription="@T("Default custom target language")" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedOtherLanguage)" TextUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedOtherLanguage = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.PreselectedOtherLanguage, out var meta) && meta.IsLocked"/>
}
<ConfigurationSelect OptionDescription="@T("Default audience profile")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceProfile)" Data="@ConfigurationSelectDataFactory.GetSlideBuilderAudienceProfileData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceProfile = value)"/>
<ConfigurationSelect OptionDescription="@T("Default audience age group")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceAgeGroup)" Data="@ConfigurationSelectDataFactory.GetSlideBuilderAudienceAgeGroupData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceAgeGroup = value)"/>
<ConfigurationSelect OptionDescription="@T("Default audience organizational level")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceOrganizationalLevel)" Data="@ConfigurationSelectDataFactory.GetSlideBuilderAudienceOrganizationalLevelData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceOrganizationalLevel = value)"/>
<ConfigurationSelect OptionDescription="@T("Default audience expertise")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceExpertise)" Data="@ConfigurationSelectDataFactory.GetSlideBuilderAudienceExpertiseData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceExpertise = value)"/>
<ConfigurationOption OptionDescription="@T("Show source references by default?")" LabelOn="@T("Source references are visible")" LabelOff="@T("Source references are hidden")" State="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.ShowSourceReferences)" StateUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.ShowSourceReferences = value)"/>
<ConfigurationOption OptionDescription="@T("Optimize large visual assets by default?")" LabelOn="@T("Large visual assets are optimized")" LabelOff="@T("Visual assets keep their original size")" State="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.OptimizeImages)" StateUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.OptimizeImages = value)"/>
<ConfigurationMinConfidenceSelection RestrictToGlobalMinimumConfidence="true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.MinimumProviderConfidence)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.MinimumProviderConfidence = value)"/>
<ConfigurationProviderSelection Component="Components.VISUAL_BRIEFING_ASSISTANT" Data="@this.AvailableLLMProviders" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedProvider)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedProvider = value)"/>
<ConfigurationSelect OptionDescription="@T("Default profile")" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedProfile = value)"/>
<ConfigurationSelect OptionDescription="@T("Default audience profile")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceProfile)" Data="@ConfigurationSelectDataFactory.GetSlideBuilderAudienceProfileData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceProfile = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.PreselectedAudienceProfile, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Default audience age group")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceAgeGroup)" Data="@ConfigurationSelectDataFactory.GetSlideBuilderAudienceAgeGroupData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceAgeGroup = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.PreselectedAudienceAgeGroup, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Default audience organizational level")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceOrganizationalLevel)" Data="@ConfigurationSelectDataFactory.GetSlideBuilderAudienceOrganizationalLevelData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceOrganizationalLevel = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.PreselectedAudienceOrganizationalLevel, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Default audience expertise")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceExpertise)" Data="@ConfigurationSelectDataFactory.GetSlideBuilderAudienceExpertiseData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedAudienceExpertise = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.PreselectedAudienceExpertise, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Show source references by default?")" LabelOn="@T("Source references are visible")" LabelOff="@T("Source references are hidden")" State="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.ShowSourceReferences)" StateUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.ShowSourceReferences = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.ShowSourceReferences, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Optimize large visual assets by default?")" LabelOn="@T("Large visual assets are optimized")" LabelOff="@T("Visual assets keep their original size")" State="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.OptimizeImages)" StateUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.OptimizeImages = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.OptimizeImages, out var meta) && meta.IsLocked"/>
<ConfigurationMinConfidenceSelection RestrictToGlobalMinimumConfidence="true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.MinimumProviderConfidence)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.MinimumProviderConfidence = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.MinimumProviderConfidence, out var meta) && meta.IsLocked"/>
<ConfigurationProviderSelection Component="Components.VISUAL_BRIEFING_ASSISTANT" Data="@this.AvailableLLMProviders" SelectedValue="@(() => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedProvider)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedProvider = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.PreselectedProvider, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Default profile")" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.VisualBriefing.PreselectedProfile = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.VisualBriefing, x => x.PreselectedProfile, out var meta) && meta.IsLocked"/>
</MudPaper>
</DialogContent>
<DialogActions>
@@ -406,6 +406,13 @@ CONFIG["SETTINGS"] = {}
-- but users can still choose another start page in the app settings.
-- CONFIG["SETTINGS"]["DataApp.StartPage.AllowUserOverride"] = true
-- Configure whether AI Studio saves energy while the AI writes its answers.
-- Allowed values are: true, false
-- When set to true, streamed answers are updated once every third second instead of as soon
-- as new text arrives. This helps on battery-powered devices and on virtual desktops, where
-- every redraw of the screen is costly.
-- CONFIG["SETTINGS"]["DataApp.IsSavingEnergy"] = true
-- Configure whether the quick start guide is shown on the welcome page.
-- CONFIG["SETTINGS"]["DataApp.ShowQuickStartGuide"] = false
@@ -14,6 +14,15 @@ public static partial class ManagedConfiguration
private static ILogger Log => Program.LOGGER_FACTORY.CreateLogger(nameof(ManagedConfiguration));
/// <summary>
/// All registered settings, in no particular order.
/// </summary>
/// <remarks>
/// The data classes register their settings when the settings data is created. Once it exists,
/// this covers every managed setting, whatever its type.
/// </remarks>
internal static IEnumerable<ConfigMetaBase> RegisteredSettings => METADATA.Values.OfType<ConfigMetaBase>();
/// <summary>
/// Attempts to retrieve the configuration metadata for a given configuration selection and
/// property expression (enum-based).
@@ -325,11 +334,8 @@ public static partial class ManagedConfiguration
var wasChanged = false;
var registeredSettingNames = new HashSet<string>(StringComparer.Ordinal);
foreach (var config in METADATA.Values)
foreach (var configMeta in RegisteredSettings)
{
if (config is not ConfigMetaBase configMeta)
continue;
registeredSettingNames.Add(configMeta.SettingName);
//
@@ -37,7 +37,7 @@ Configuration plugins provide three kinds of values:
- **Live plugin content:** complex Lua tables that implement `ILivePluginContent` and are read live from running plugins instead of being persisted to `ConfigurationData`. Examples include `MANDATORY_INFOS` and `INTRODUCTIONS`. If live plugin content creates persistent side data, add a dedicated cleanup path for that side data, like mandatory-info acceptances.
When adding configuration plugin capabilities:
- For managed settings, update the corresponding data class in `app/MindWork AI Studio/Settings/DataModel/` to call `ManagedConfiguration.Register(...)` and process the setting in `PluginConfiguration.TryProcessConfiguration`. Cleaning up the setting when its configuration plugin was removed needs no extra step: `ManagedConfiguration.CleanupLeftOverManagedConfigurations(...)` iterates all registered settings. Do not add per-setting cleanup calls to `PluginFactory.Loading.LoadAll`.
- For managed settings, update the corresponding data class in `app/MindWork AI Studio/Settings/DataModel/` to call `ManagedConfiguration.Register(...)` and process the setting in `PluginConfiguration.TryProcessConfiguration`. Cleaning up the setting when its configuration plugin was removed needs no extra step: `ManagedConfiguration.CleanupLeftOverManagedConfigurations(...)` iterates all registered settings. Do not add per-setting cleanup calls to `PluginFactory.Loading.LoadAll`. `ManagedSettingsCoverageTests` fails for a registered setting which no configuration plugin can manage; a setting which must stay out of their reach goes into its list `EXEMPT_SETTINGS`, together with the reason.
- For managed configuration objects, add the type to `PluginConfigurationObjectType.cs`, persist the objects in the appropriate `ConfigurationData` collection, and add a descriptor to `PluginConfigurationObjectDescriptors` and its list `ALL`: its Lua table, its parser, the collection, the next number, and where secrets are kept. Parsing and cleanup need no extra call; `PluginConfigurationObjectDescriptorsTests` fails for a type without a descriptor.
- For live plugin content, add a data type implementing `ILivePluginContent`, parse it in `PluginConfiguration`, expose it through `PluginFactory`, and add any required cleanup only for persistent side data.
- Always document the new capability in `app/MindWork AI Studio/Plugins/configuration/plugin.lua`.
@@ -233,6 +233,9 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
// Config: what should be the start page?
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.StartPage, this.Id, settingsTable, dryRun);
// Config: save energy while the AI streams its answers?
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.IsSavingEnergy, this.Id, settingsTable, dryRun);
// Config: show prompt-injection alert dialogs?
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.ShowPromptInjectionAlert, this.Id, settingsTable, dryRun);
@@ -447,6 +450,19 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.MinimumProviderConfidence, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.BatchProcessing, x => x.PreselectedProvider, Guid.Empty, this.Id, settingsTable, dryRun);
// Config: Visual Briefing Assistant defaults?
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.PreselectedProvider, Guid.Empty, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.PreselectedProfile, Guid.Empty, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.PreselectedTargetLanguage, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.PreselectedOtherLanguage, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.PreselectedAudienceProfile, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.PreselectedAudienceAgeGroup, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.PreselectedAudienceOrganizationalLevel, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.PreselectedAudienceExpertise, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.ShowSourceReferences, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.OptimizeImages, this.Id, settingsTable, dryRun);
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.MinimumProviderConfidence, this.Id, settingsTable, dryRun);
// Config: transcription provider?
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.UseTranscriptionProvider, Guid.Empty, this.Id, settingsTable, dryRun);
@@ -38,6 +38,7 @@
- Improved the Batch Processing assistant, which now leaves out hidden files and folders as well as build output and downloaded dependencies, such as the folder `node_modules`. You can change both in the expert settings of the assistant.
- Improved local data sources for folders and the Batch Processing assistant, which now leave out files larger than 1 GB, such as images of virtual machines. You can change this limit.
- Improved the organization-wide management of the Batch Processing assistant. IT departments can now preset whether it includes hidden files and folders, which files and folders it leaves out, and how large a file may be, either fixed or as a default which users may change.
- Improved the organization-wide management of the app settings. IT departments can now also set whether AI Studio saves energy while the AI writes its answers, which helps on virtual desktops, for example.
- Improved attaching files to a chat. You can now attach any file that contains readable text, for example, program code or configuration files of a kind AI Studio did not know before.
- Changed the banner on the start page to a fresh design that shows what AI Studio can do today.
- Fixed a warning about the confidence level that stayed in the dialog for adding a local data source, even after you chose an embedding provider that meets it. The dialog now checks again whenever you change either of them, so a warning also appears when you switch to a provider that falls short.
@@ -77,6 +78,7 @@
- Fixed the security notice about removed instructions (prompt injection) appearing only minutes later while AI Studio updated a data source in the background. The notice also mixed your own attachments with files of the data source. Now each notice appears as soon as AI Studio has read the content, for example, the attachments of your message, and it names only that content.
- Fixed the security notice about removed instructions (prompt injection) appearing several times for one answer when AI Studio checks which passages of your data sources fit your question: once for every passage it checked, and once more for all of them together. You now get one notice per answer, and a file counts once, however many of its passages were affected.
- Fixed the example configuration for IT departments, which spelled llama.cpp in a way AI Studio does not accept. AI Studio ignored a self-hosted provider set up with that spelling. The example now also names only the self-hosted servers which can create embeddings or transcribe audio.
- Fixed the configuration of your organization not applying its defaults for the Visual Briefing assistant, such as the provider, the language, or the audience. The settings of the assistant now also show which defaults your organization has locked.
- Fixed exporting an LLM provider or an embedding provider with a tokenizer. On Windows, the export broke the configuration plugin, and on every system, the plugin ignored the tokenizer because the export named a folder on your computer. The export now names the place in your configuration plugin where the tokenizer file belongs.
- Fixed building AI Studio from source on Windows, where the checks that run before every build failed because two of them expected the line breaks and file paths of macOS and Linux. Thanks, Paul Koudelka (`PaulKoudelka`), for this contribution.
- Fixed a problem on Windows where AI Studio could fail to read the configuration of your organization when the name of an environment variable on your computer contained umlauts or other special characters.