First check what / actually does when Googlebot hits it. If it serves the English content directly, or if it 302s to /en based on Accept-Language, then as far as Google is concerned / is your English page and /en is the duplicate. That is usually the cause, not a broken canonical tag.
Googlebot crawls mostly from US IPs and does not send Accept-Language the way a browser does, so language detection redirects tend to resolve to English. You end up with two URLs serving identical content, Google consolidates them, and it keeps the shorter one.
In Search Console open URL Inspection on /en and compare User-declared canonical against Google-selected canonical. If they differ, Google is overriding your tag, which it is allowed to do. The canonical link is a hint, not an instruction.
If you want /en to win you have to make every signal agree, not just the tag:
1. 301 redirect / to /en. Permanent, not 302.
2. Self-referencing canonical on /en pointing at /en. Check the rendered HTML rather than the template, because multilingual plugins often rewrite this.
3. Sitemap lists /en and never /.
4. Internal links, logo link and menu home link all point to /en.
5. hreflang set is reciprocal and x-default points at /en rather than /.
Miss one of those and Google keeps picking /, because the rest of your signals still say / is the real page.
Before you do any of that though, ask whether you actually want it. Having / as the default home with /en, /fr, /de underneath is the more common setup and it works fine. Redirecting / to /en adds a hop on every visit to your root domain and gains you nothing in rankings. If the only reason is that Search Console displays / instead of /en, that is a reporting preference rather than a problem.
What is / returning right now, a 200 with English content or a redirect?