DIisplay V2 does not support setting URL type as a value?

error[E04007]: incompatible types
   ┌─ ./sources/display.move:31:9
   │
31 │         display_registry::set(&mut file_display, &file_cap, string::utf8(b"thumb_url"), url::new_unsafe_from_bytes(b"https://walrus.tusky.io/{thumb_blob_id}"));
   │         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid call of 'sui::display_registry::set'. Invalid argument for parameter 'value'
   │
   ┌─ /home/du/.move/git/https___github_com_MystenLabs_sui_git_384b1c7ff289b32ed2664f29dc47dbc879fa43ee/crates/sui-framework/packages/sui-framework/sources/url.move:22:54
   │
22 │ public fun new_unsafe_from_bytes(bytes: vector<u8>): Url {
   │                                                      --- Given: 'sui::url::Url'
   │
   ┌─ /home/du/.move/git/https___github_com_MystenLabs_sui_git_384b1c7ff289b32ed2664f29dc47dbc879fa43ee/crates/sui-framework/packages/sui-framework/sources/registries/display_registry.move:81:85
   │
81 │ public fun set<T>(display: &mut Display<T>, _: &DisplayCap<T>, name: String, value: String) {
   │                                                                                     ------ Expected: 'std::string::String'

As shown in the error, when I use Display V2 to set the URL of an NFT, it prompts me that I cannot use the Url type. Is there something wrong with my usage method, or does Display indeed not support the Url type?
If possible, could you provide an example of code using Display V2?
thx

Your diagnosis is correct. This is by design, not a bug in your code.

display_registry::set only accepts String values, as shown in the function signature from the error:

public fun set<T>(display: &mut Display<T>, _: &DisplayCap<T>, name: String, value: String)

The Display<T> struct stores fields as VecMap<String, String> — all values are strings. sui::url::Url is a separate type and is not accepted.

The fix is simple: convert the Url to a String (or just pass a String directly). For template URLs in Display, you should pass the raw string anyway, since Display templates use {field_name} substitution syntax at render time — you wouldn’t want a resolved Url object as the static value.

Here’s the corrected example:

module my_package::my_nft;

use std::string::String;
use sui::display_registry::{Self, Display, DisplayCap};

// ✅ Pass a String directly — no Url wrapping needed
display_registry::set(
    &mut file_display,
    &file_cap,
    b"thumb_url".to_string(),
    b"https://walrus.tusky.io/{thumb_blob_id}".to_string(),
);

The {thumb_blob_id} placeholder is a Display template variable — at render time, the Display engine substitutes the actual thumb_blob_id field value from your NFT object. Passing a Url type here doesn’t make sense and isn’t supported.

Full minimal Display V2 setup example:

module my_package::my_nft;

use std::string::String;
use sui::display_registry;
use sui::package;

public struct MY_NFT has drop {}

public struct FileNFT has key, store {
    id: sui::object::UID,
    name: String,
    thumb_blob_id: String,
}

fun init(otw: MY_NFT, ctx: &mut sui::tx_context::TxContext) {
    let publisher = package::claim(otw, ctx);

    let (mut display, cap) = display_registry::new<FileNFT>(&publisher, ctx);

    display_registry::set(
        &mut display,
        &cap,
        b"name".to_string(),
        b"{name}".to_string(),
    );
    display_registry::set(
        &mut display,
        &cap,
        b"thumb_url".to_string(),
        b"https://walrus.tusky.io/{thumb_blob_id}".to_string(),
    );

    sui::transfer::public_transfer(publisher, ctx.sender());
    sui::transfer::public_share_object(display);
    sui::transfer::public_transfer(cap, ctx.sender());
}

Reference: Display Registry docs | Display V2 overview


:robot: This is an automated response from the Sui DevRel Agent. If this doesn’t fully address your question, please reply and a team member will follow up.

1 Like