Hi,
The conflict isn't on the refresh token itself. When several tabs renew at the same time - the silent /connect/authorize calls in your screenshot - each one creates a new token under the same authorization. ABP currently treats that foreign key as a navigation change and updates the shared authorization's ConcurrencyStamp on every one of those token inserts.
The first request updates the authorization and wins; the concurrent ones still hold the previous stamp, so their update affects 0 rows and EF Core throws the DbConcurrencyException you're seeing.
The framework already excludes OpenIddictApplication from this, but not OpenIddictAuthorization. You can add it in your *EntityFrameworkCoreModule (the .EntityFrameworkCore project):
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.OpenIddict.Authorizations;
// inside ConfigureServices
Configure<AbpEntityChangeOptions>(options =>
{
options.IgnoredNavigationEntitySelectors.Add(
"DisableOpenIddictAuthorization",
type => type == typeof(OpenIddictAuthorization));
});
After this, creating a token no longer touches the shared authorization, so concurrent renewals from multiple tabs stop colliding. Nothing needs to change on the Angular side.
We'll also add OpenIddictAuthorization to the framework defaults.
Thanks
Hi,
For a single (non-tiered) host, nothing new is required — it enqueues and drains its own jobs, same as v8. In the 10.4.1 tiered template, AuthServer and Web.Host don't execute background jobs; HttpApi.Host does. So the first question is: single host, or separated? That decides which process should be draining the queue.
Look at the rows that should be getting processed — IsAbandoned = 0 and NextTryTime already due:
select top 50 Id, JobName, ApplicationName, TryCount, LastTryTime, NextTryTime, IsAbandoned, CreationTime
from AbpBackgroundJobs
order by CreationTime desc;
TryCount = 0 / LastTryTime null on a due row that's been sitting a while → no attempt has been persisted. Usually that means it hasn't run, but the same state also shows while a job is executing, hung, or the host exited mid-run — TryCount is only written back on a failed attempt, and on success the row is deleted.TryCount > 0 → at least one attempt failed and was written back (deserialization, DI, or the email sender / SMTP) — that shows up in the logs. IsAbandoned = 1 means the framework stopped retrying it.NextTryTime in the future → not due yet, so it's waiting rather than stuck (an initial enqueue delay when TryCount = 0, a retry backoff when TryCount > 0).If due rows just sit there unprocessed, check the host that's supposed to drain them (HttpApi.Host in the tiered template):
AbpBackgroundJobOptions.IsJobExecutionEnabled and AbpBackgroundWorkerOptions.IsEnabled aren't set to false on that host — either one stops the worker from starting, with no error. The tiered template sets IsJobExecutionEnabled = false on AuthServer/Web.Host, so check it wasn't carried over to the wrong project during the upgrade.AbpBackgroundJobs connection string and fall back to Default — if the processing host points at a different DB, it drains a different AbpBackgroundJobs table while the rows you're looking at just sit.ApplicationName (new since v8): the worker only takes rows whose ApplicationName matches its own AbpBackgroundJobWorkerOptions.ApplicationName. Defaults are null on both sides and match; if you set it, or point several apps at the same table, compare the ApplicationName column on the stuck rows with that host's value.If none of that stands out, the fastest way for us to spot it is the project — a minimal reproduction where the jobs pile up, in a private GitHub repo (invite https://github.com/maliming) or zipped to liming.ma@volosoft.com.
Thanks
Hi,
2FA got some changes in 10.4, so before pointing at SMTP let's narrow down exactly where it stops. Two things to keep in mind while you check:
Could you share:
The server log at the submit step plus the answers above will tell us whether it stops at code generation, at enqueue, or at the actual SMTP send. If the default logs aren't detailed enough, enable Debug-level logging first: https://abp.io/support/questions/8622/How-to-enable-Debug-logs-for-troubleshoot-problems
Thanks
Hi,
Keeping the pending migrations and folding only the already-deployed ones into a baseline is the right idea in spirit, but leaving the pending files in place while you squash the earlier ones doesn't work cleanly. EF orders migrations by their MigrationId timestamp, not by file or commit order — a freshly generated baseline gets a newer timestamp than your existing pending migrations, so those pending ones would sort before the baseline instead of after it, and the chain breaks. The model snapshot and history bookkeeping are easy to get wrong here too.
The clean approach is to re-baseline from a point where there are no pending migrations at all — generate it against the code that matches production (your production release branch/commit), not your dev head. In practice you do it once the currently-pending features have been promoted, so dev and production line up:
Migrations folder and run dotnet ef migrations add Initial on that aligned state.__EFMigrationsHistory on each existing database (they're all at that same point now).You can squash only the already-deployed prefix while keeping the dev-ahead migrations, but that means manually fixing the baseline's MigrationId ordering, the model snapshot, and any custom SQL or data steps inside those migrations — error-prone enough that lining dev and production up first is the safer route.
Your approval → test → deploy process itself doesn't change; this is a one-time re-baseline done at a release boundary.
Thanks
Hi,
There's no fixed schedule for it, and no permanent fix on EF's side — EF doesn't compress or archive old migrations for you. As long as you keep adding migrations to the same project that gets compiled on every build, the cost grows back, since each migration carries its own full generated model snapshot. So instead of a set interval, re-baseline again when your build times noticeably climb; a major release is a natural point to do it.
What slows the growth in between is consolidating migrations while they're still local — before they've been applied to any database you can't freely recreate (staging, shared dev, production) and before another branch depends on them. In that window you can collapse several small migrations into one: remove them in reverse order and re-add a single combined migration.
dotnet ef migrations remove
Two caveats when you do this: if a migration is already applied to your local database, roll it back (or recreate the DB) first, or remove will refuse; and any handwritten SQL or data migration won't reappear in the regenerated file, so carry those over by hand.
The final schema stays the same either way — this is only about how many migration files the compiler has to process.
Thanks
Hi,
Glad you tracked it down, and thanks for posting the root cause — it's a useful one. Setting RemoteServices.Default.BaseUrl from IWebAssemblyHostEnvironment.BaseAddress at runtime is exactly the right move when the same app is served from more than one origin; a fixed BaseUrl in appsettings.json always pins the client to a single origin and breaks cross-origin from the others.
Good to hear the stock Autofac + InteractiveAuto setup runs clean with trimming back on. Feel free to reopen if anything else comes up.
Thanks
Hi,
On the licensing question first: an active Business license is enough. There's no separate MCP entitlement, license tier, or feature toggle for the cloud MCP server, so this isn't a per-organization enablement gap on the license side.
The "No tools available" message means the CLI did get a response back, but couldn't read any tool definitions out of it. To see what's actually coming back for your organization, could you grab two things. Run them inside the same container/shell where abp mcp fails, so we're looking at the exact ~/.abp/cli config that command uses (running it elsewhere would read a different config):
-i includes the status line so we can see the exact status code and body (the response itself doesn't contain your token).cat ~/.abp/cli/mcp-config.json 2>/dev/null || echo "no custom config"
TOKEN=$(sed $'s/\xef\xbb\xbf//' ~/.abp/cli/access-token.bin)
curl -i -H "Authorization: Bearer $TOKEN" https://mcp.abp.io/tools
If you also run the CLI directly on Windows and hit the same error there, the PowerShell equivalent is:
Get-Content "$HOME\.abp\cli\mcp-config.json" -ErrorAction SilentlyContinue
$token = (Get-Content "$HOME\.abp\cli\access-token.bin" -Raw).Trim()
curl.exe -i -H "Authorization: Bearer $token" https://mcp.abp.io/tools
That output tells us whether the endpoint is returning an empty list, a redirect, or an auth/license rejection — three very different things to chase.
Two notes: abp mcp get-config only prints the local stdio config and never calls the server, so it working doesn't tell us anything about this endpoint. And the ABP_MCP_LOG_LEVEL env var is capped at info level in the released CLI and won't print the raw HTTP response, so the curl above is the quicker way to capture it.
We'll check the server-side logs for your organization in parallel. Send those two outputs over and we'll pin it down.
Thanks
Hi,
First, the setup itself is fine. Autofac + dynamic HTTP client proxies + InteractiveAuto is exactly what the ABP 10.4 Blazor Web App template ships in the .Client project, so you didn't misconfigure anything there. I'd revert the Autofac removal (put AbpAutofacWebAssemblyModule back in [DependsOn], restore the Volo.Abp.Autofac.WebAssembly package and options.UseAutofac(), and drop the ValidateScopes/ValidateOnBuild change). Removing Autofac is what pushed you into the captive-dependency error and then the null IClientScopeServiceProviderAccessor, and it isn't a supported swap for this template — disabling scope validation only hides the captive dependency, it doesn't fix the lifetimes.
About the main error: Method not found: Autofac.Core.KeyedService.get_AnyKey() is worth reading carefully. That member exists in every Autofac version in play here (9.1.0 with Autofac.Extensions.DependencyInjection 11.0.0), and it isn't removed by trimming. A "method not found" on a member that's actually present almost always means the Autofac.dll the browser is running doesn't match the Autofac.Extensions.DependencyInjection.dll that calls it — i.e. the WASM assets the browser loaded came from more than one build (stale browser/service-worker cache, a CDN edge, or a non-atomic deploy), rather than a real Autofac-vs-WASM incompatibility.
The "first load is fine, every later load fails, and a plain F5 triggers it" pattern fits that: the first request renders on the server (fresh, server-side assemblies), and the later loads run the cached WASM bundle in the browser.
So I'd start here:
_framework asset the browser fetches comes from the same build.dotnet publish -c Release -p:PublishTrimmed=false (and no RunAOTCompilation). That's the documented baseline for the Blazor project and takes the linker/AOT out of the picture as a variable.If it still reproduces on a clean, cache-free deploy, send these over and we'll take it from there:
dotnet list <YourProject>.Blazor.WebApp.Client package --include-transitive and dotnet --info.Directory.Build.* / .pubxml, and the effective values of PublishTrimmed, TrimMode, RunAOTCompilation.Autofac.dll / Autofac.Extensions.DependencyInjection.dll versions actually sitting in the deployed _framework folder.Program.cs (both the Autofac and no-Autofac versions).If any of that is large, you can email it to liming.ma@volosoft.com, or share a minimal project that reproduces it on a fresh 10.4 template.
Thanks
Hi,
The Admin Console ships as a packaged React app, but you don't have to live with its default look — it's designed to be re-skinned from the host side, and the React template already wires this up for you.
Your generated solution already contains a wwwroot/theme-override.css file, and the host's appsettings.json already points to it:
{
"AdminConsole": {
"ThemeOverrideCssPath": "/theme-override.css"
}
}
So the styling entry point is that file — just edit it. The Admin Console is built on a shadcn/Tailwind CSS-variable theme, and the generated theme-override.css already defines the full light/dark token set (:root and .dark). Change the values you want and the whole UI follows — colors, surfaces, borders, radius:
:root {
--primary: oklch(0.62 0.19 260);
--primary-foreground: oklch(1 0 0);
--background: oklch(1 0 0);
--secondary: oklch(0.97 0.01 260);
--accent: oklch(0.96 0.02 260);
--border: oklch(0.92 0.01 260);
--radius: 0.375rem;
}
.dark {
--primary: oklch(0.68 0.17 260);
--background: oklch(0.18 0.02 268);
}
The file already lists the other tokens too (--foreground, --card, --popover, --muted, --destructive, --input, --ring, etc.), so you can adjust as much or as little as you want. This file is loaded after the console's own stylesheet, so overriding these theme variables takes precedence. If you go beyond the variables and target specific elements with your own selectors, that works as well — just keep in mind the internal class names can change across package upgrades, and a few third-party widgets (code editor, date picker) use their own colors that the tokens won't fully drive.
For branding, use ApplicationName and LogoUrl in the same AdminConsole section. An SVG logo is the safest choice, since the same URL is also used for the favicon. There's no separate font option, but you can set body { font-family: ... } in the override CSS.
If you'd rather manage these at runtime instead of in config, set CustomizationPermissionName (the template already defines AdminConsole.Customization). Users with that permission get a Customization page inside the Admin Console for the application name, logo, and theme-override CSS path, stored as settings.
On how far this goes: the packaged Admin Console is intentionally kept as an upgradeable surface, so the supported customization model is host-side branding plus the CSS override above — that's the path for restyling it. It's not meant to be edited as source in a layered/single-layer solution, so for structural UI changes (custom layout or components, not just styling) the place to build those is your own React app. The microservice template is the one shape where the Admin Console is shipped as a real source app (apps/react-admin-console/).
If there's a specific change you have in mind that the CSS override can't reach, tell us exactly what you want to adjust and we'll point you at the best way to do it.
Thanks
Hi,
Confirmed, this is a real bug in the classic Lepton Angular theme. The missing classes are the cause — the layout condition itself evaluates correctly, so it isn't a layout-detection issue, and it isn't specific to Style 4.
The nested submenu <ul> in the sidebar routes component applies its visibility class through an Angular object binding, roughly:
<ul class="dropdown-ul" [class]="{ 'd-block overflow-hidden': ... }">
That key is a single string holding two space-separated classes (d-block overflow-hidden). Angular's native [class] binding doesn't split a multi-word key — it drops any object key that contains a space before it reaches the DOM. [ngClass] splits on whitespace and applies both classes, so when the binding was switched from [ngClass] to [class], d-block stopped landing on the <ul>. Without d-block, the theme's nested-menu rule (.lp-opened-sidebar ... > ul ul { display: none; }) keeps the <ul> at display: none, even though the route is current and expandedRoutes is updated.
To your three questions:
d-block overflow-hidden because the class binding silently discards the multi-word key, not because of the sidebar/body layout state. For the opened left sidebar, the placement/small-screen condition resolves to "apply the class".{ 'd-block': ..., 'overflow-hidden': ... } (or going back to [ngClass]) makes both classes apply again.One note on scope: this isn't new in 10.5. The binding changed in 10.2.0 and is still present in 10.6.0-rc.1, and it affects every Lepton style (1–6), since they all share the same nested-ul hide rule — Style 4 is just the one you're on. That's also why 10.6.0-rc.1 doesn't change anything here.
Your CSS workaround is a valid interim fix, so you can keep it until the patch ships.
Thanks