tokio-rs / axum

Ergonomic and modular web framework built with Tokio, Tower, and Hyper

Repository from Github https://github.comtokio-rs/axumRepository from Github https://github.comtokio-rs/axum

Path extractor fails when routes are loaded from a dynamic library (but Json extractor works)

Triment opened this issue · comments

  • I have looked for existing issues (including closed) about this

Bug Report

Version

axum v0.8.4

Platform

Darwin asdeMac-Pro.lan 24.4.0 Darwin Kernel Version 24.4.0: Fri Apr 11 18:28:23 PDT 2025; root:xnu-11417.101.15~117/RELEASE_X86_64 x86_64

Description

Hi, I’m encountering a strange issue with Axum 0.8 when using dynamic libraries (plugins), Loading by libloading = "0.8.8".

In the main program, path parameter extraction works as expected.

When I load routes from a dynamic library (compiled separately), Path extractor fails with the error:

No paths parameters found for matched route
//In the plugin
Router::new()
            //.route("/", axum::routing::get(home))
            .route("/assets/{*path}", axum::routing::get(async |req: Request| { //It's working
                let path = req.uri().path();
                format!("assets/{}", path)
            }))

Router::new()
            //.route("/", axum::routing::get(home))
            .route("/assets/{*path}", axum::routing::get(async|Path(path): Path<String>|{ //throw error
                path 
            }))
Router::new()
            //.route("/", axum::routing::get(home))
            .route("/assets/{*path}", axum::routing::post(async |Json(HelloRequest { name }): Json<HelloRequest>| {// It's working
                format!("hello {}", name)
            }))

Main program code

        let plugin = plugin_loader::load_plugins()?;//Dynamic libraries 
        let plugin_routes = plugin.routes();
        app = app.nest("/plugins/hello/", plugin_routes);
        let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
        let server = axum::serve(listener, app).with_graceful_shutdown(async move {
            shutdown_recv.recv().await;
        });

I'm sorry, but what you are doing is not supported by Rust. You can't compile two binaries / shared libraries independently (even with the same compiler version) and use "regular" Rust types / code across that boundary¹. It has nothing to do with axum really, except that axum happens to rely on a type-map for path arguments which probably trips up your code because the type-id of the thing we store / read internally is different within your plugin and main binary.

¹ you would have to make sure to constrain yourself to #[repr(C)] types, for globals used by both it's complicated and for type-id I don't know if there is any solution other than avoiding it completely