GraphiteEditor/Graphite · error · syn::Error

value for key {k} was already given

Error message

value for key {k} was already given

What it means

The `#[hint(key = "value")]` helper attribute feeds the `Hint` derive a key→string map (mouse-button tooltips etc.). Keys are collected across every #[hint] attribute on the item into one map, and each key must appear exactly once; the first occurrence wins silently, and every subsequent occurrence raises this compile-time error pointing at the duplicate literal.

Source

Thrown at proc-macros/src/hint.rs:24

fn parse_hint_helper_attrs(attrs: &[Attribute]) -> syn::Result<(Vec<LitStr>, Vec<LitStr>)> {
	fold_error_iter(
		attrs
			.iter()
			.filter(|a| a.path().get_ident().is_some_and(|i| i == "hint"))
			.map(|attr| attr.parse_args::<AttrInnerKeyStringMap>()),
	)
	.and_then(|v: Vec<AttrInnerKeyStringMap>| {
		fold_error_iter(AttrInnerKeyStringMap::multi_into_iter(v).map(|(k, mut v)| match v.len() {
			0 => panic!("internal error: a key without values was somehow inserted into the hashmap"),
			1 => {
				let single_val = v.pop().unwrap();
				Ok((LitStr::new(&k.to_string(), Span::call_site()), single_val))
			}
			_ => {
				// the first value is ok, the other ones should error
				let after_first = v.into_iter().skip(1);
				// this call to fold_error_iter will always return Err with a combined error
				fold_error_iter(after_first.map(|lit| Err(syn::Error::new(lit.span(), format!("value for key {k} was already given"))))).map(|_: Vec<()>| unreachable!())
			}
		}))
	})
	.map(|v| v.into_iter().unzip())
}

pub fn derive_hint_impl(input_item: TokenStream2) -> syn::Result<TokenStream2> {
	let input = syn::parse2::<DeriveInput>(input_item)?;

	let ident = input.ident;

	match input.data {
		Data::Enum(data) => {
			let variants = data.variants.iter().map(|var: &Variant| two_segment_path(ident.clone(), var.ident.clone())).collect::<Vec<_>>();

			let hint_result = fold_error_iter(data.variants.into_iter().map(|var: Variant| parse_hint_helper_attrs(&var.attrs)));

			hint_result.map(|hints: Vec<(Vec<LitStr>, Vec<LitStr>)>| {

View on GitHub (pinned to c507b35645)

Solutions

  1. Find the duplicated key named in the error message and delete all but one `key = "value"` pair.
  2. If two hints were intentional, rename the second occurrence to a distinct key.
  3. After fixing, run `cargo check` again — only one duplicate is reported per pass, so repeat until clean.

Example fix

// before
#[derive(Hint)]
enum Tool {
	#[hint(lmb = "Draw", lmb = "Erase")]
	Brush,
}

// after
#[derive(Hint)]
enum Tool {
	#[hint(lmb = "Draw")]
	Brush,
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Writing the same key twice in one attribute (`#[hint(rmb = "foo", rmb = "bar")]`), or repeating a key across stacked attributes (`#[hint(a = "1")] #[hint(a = "2")]`) on the same enum variant or struct. Only the extras after the first are errored.

Common situations: Merge conflicts resolved by keeping both sides of a hint list, copy-pasting a hint attribute and editing only the value while forgetting the key, or tooling that concatenates hint attributes.

Related errors


AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16). Data as JSON: /api/errors/1cec1e51cbe26237. Report an issue: GitHub.