Configs in internal_preferences does not work in Debian 13 in Brave Browser v1.96.59

Description of the issue: I am not able to get initial_preferences to work in linux. I copied the below into /opt/brave.com/brave/initial_preferences and also to /etc/brave/initial_preferences. Both does not work. When I check brave://prefs-internals, the default values are not changed.


{
    "brave": {
        "has_seen_brave_welcome_page": true,
        "new_tab_page": {
            "show_background_image": false,
            "show_branded_background_image": false
        }
    }
}

How can this issue be reproduced?

  1. In a linux desktop, create a file /opt/brave.com/brave/initial_preferences with values in the code block above.

  2. Open brave browser and go to brave://prefs-internals and search for has_seen_brave_welcome_page. The value should be false, but it’s true. Same is applicable to show_background_image and show_branded_background_image

Expected result: The values in brave://prefs-internals should match values in internal_preferences file.

Brave Version( check About Brave): Brave 1.96.59 (Official Build) (x86_64)

Additional Information: The linux os is Debian 13. I have some customization in /etc/brave/policies/managed/GroupPolicy.json. Not sure if presense of policies file impact initial_preferences.

Hi swing,

I traced this all the way through Chromium’s actual source code (chrome/browser/metrics/chrome_feature_list_creator.cc, function SetupInitialPrefs()):

if (!first_run::IsChromeFirstRun()) {
return;
}
installer_initial_prefs_ = first_run::LoadInitialPrefs();

This confirms it precisely: if IsChromeFirstRun() returns false (which it will, once a “first run” sentinel file already exists in your profile’s user-data-dir — created once, on the very first launch, and never removed), the initial_preferences file is never even opened. Not a parsing issue, not a path issue — the code returns before reading the file at all.

So the practical test: does it work on a genuinely fresh profile where that sentinel doesn’t exist yet? If yes, that confirms this exactly.

There’s possibly a --force-first-run command-line flag that re-triggers this path regardless of the sentinel (referenced in a comment near that check), but I haven’t verified its full behavior — worth testing cautiously if you want to explore it, rather than treating it as a guaranteed fix.

Hi swing,

Quick follow-up on the --force-first-run flag I mentioned — I verified its actual behavior in the source, and it’s not a clean drop-in fix on its own.

Using --force-first-run alone re-triggers the entire First Run Experience UI (sign-in/onboarding), not just the initial_preferences processing — confirmed in chrome/browser/ui/startup/first_run_service.cc, where the same flag that makes IsChromeFirstRun() true also feeds directly into ShouldOpenFirstRun().

Chromium’s own tests use it combined with --no-first-run specifically to avoid that: brave --force-first-run --no-first-run (both together, not contradictory despite the names) should get the file processed without reopening the full onboarding flow. Worth testing that combination specifically rather than --force-first-run alone.

Also note: this flag is stripped automatically on any auto-restart, so it’s designed for one-off testing, not for a permanent launcher shortcut.

The initial_preferences file is only read once upon the very first creation of a user profile (when the Preferences file doesn’t yet exist), and placing it in /opt/brave.com/brave/ or /etc/brave/ will not affect an already existing profile. Additionally, your managed JSON policies in /etc/brave/policies/managed/ take absolute precedence over initial_preferences; if any overlapping settings are controlled wisely login via policies, those values will override everything else. To test your configuration properly, completely wipe or rename your existing browser profile directory (~/.config/BraveSoftware/Brave-Browser/) so a fresh profile is forced to read the initial preferences file on startup.

Thanks for the detailed response. The command brave --force-first-run --no-first-run still shows the welcome page. But the welcome page does not bother me. I just ignore it.

Edit: I removed te first run sentinal file for brave to pick up the initial_preferences file.

swing, thanks for the detailed confirmation earlier (the sentinel-file test) — that’s what let this get closed out properly. Follow-up here with the full picture on the welcome-tab side, after actually testing it (not just reading code) — sharing in case it helps someone else searching this thread later. Long post, but it’s all empirical now.

Correction on my part first: the tab is chrome://welcome/ (confirmed in brave-core source), displayed as brave://welcome in the address bar once loaded — not brave://welcome as I loosely called it earlier.

Where the pref you set (has_seen_brave_welcome_page) actually gets used: only to decide whether to redirect Japan-region users to a special variant of the welcome content (browser/ui/webui/brave_welcome_page/brave_welcome_page_ui.cc). It has nothing to do with whether the tab opens, and it gets set to true unconditionally the moment the page loads anyway.

The actual tab-opening decision (browser/ui/startup/brave_startup_tab_provider_impl.cc):

StartupTabs BraveStartupTabProviderImpl::GetDistributionFirstRunTabs(…) const {
StartupTabs tabs = StartupTabProviderImpl::GetDistributionFirstRunTabs(browser_creator);
if (first_run::IsChromeFirstRun()) {
tabs.emplace_back(GURL(kWelcomeURL));
}
return tabs;
}

This unconditionally appends the welcome tab whenever first_run::IsChromeFirstRun() is true — no pref consulted. That’s the exact same condition that gates whether initial_preferences gets read at all. So on current code, no command-line flag or pref decouples “skip the tab” from “read initial_preferences” — you get both or neither.

No admin policy purpose-built for this either — checked both Brave’s own policy set and upstream Chromium’s; nothing matches (closest upstream match, WelcomePageOnOSUpgradeEnabled, is deprecated/Windows-only/a different feature entirely).

I actually tested the one remaining theoretical option, Chromium’s generic URLBlocklist policy, rather than just reading about it:

First attempt failed silently — I used chrome://welcome/* (trailing wildcard), which loaded into the policy without any error, but did nothing: the page loaded normally. Turned out why, from reading url_matcher’s filter-parsing code (components/url_matcher/url_util.cc, FilterToComponents): the trailing /* is NOT treated as a wildcard for internal chrome:// URLs in this filter syntax — it’s parsed as a literal path-prefix string "/*", which a real navigation’s path (/) never matches. The policy UI’s own display of the saved filter even reflected this silently-mangled interpretation.

Corrected version — dropping the wildcard entirely (chrome://welcome and brave://welcome, no /*) — did work for a manual/typed navigation: navigating there in an already-running browser produced Chromium’s standard “This page is blocked — your organization doesn’t allow you to view this site” interstitial. So the policy mechanism itself is real and functional when the filter syntax is right.

But: testing the actual first-run scenario (fresh temp profile via --user-data-dir + --force-first-run, same machine-wide policy already active) — the automatically-opened welcome tab was not blocked; it loaded normally. Same policy, different code path/timing between a user-typed navigation and the browser-initiated first-run tab — I don’t know which, and I’m not going to guess. Didn’t chase it further.

Bottom line, now fully evidence-based rather than theoretical: there’s no clean way to suppress just the welcome tab while keeping initial_preferences processing, and the one policy-based workaround that exists (URLBlocklist) blocks the URL when you navigate to it manually, but not the automatic first-run tab specifically. Ignoring the tab (as you’re already doing) remains the practical answer.