{"record":{"id":"8c6df06a311935b1","repo":"sxyazi/yazi","slug":"scheme-must-be-1-20-characters-in-kebab-case-got","errorCode":null,"errorMessage":"scheme must be 1-20 characters in kebab-case, got: {s}","messagePattern":"scheme must be 1-20 characters in kebab-case, got: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"yazi-shared/src/auth/scheme.rs","lineNumber":53,"sourceCode":"}\n\nimpl PartialEq<&Self> for Scheme {\n\tfn eq(&self, other: &&Self) -> bool { self == *other }\n}\n\nimpl fmt::Display for Scheme {\n\tfn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.as_str().fmt(f) }\n}\n\nimpl FromStr for Scheme {\n\ttype Err = anyhow::Error;\n\n\tfn from_str(s: &str) -> Result<Self> {\n\t\tOk(match s {\n\t\t\t\"regular\" => Self::Regular,\n\t\t\t\"sftp\" => Self::Sftp,\n\t\t\t_ if let Some(s) = KebabCasedKey::new(s) => Self::Custom(s),\n\t\t\t_ => bail!(\"scheme must be 1-20 characters in kebab-case, got: {s}\"),\n\t\t})\n\t}\n}\n\nimpl Scheme {\n\tpub(crate) fn as_str(&self) -> &str {\n\t\tmatch self {\n\t\t\tSelf::Regular => \"regular\",\n\t\t\tSelf::Sftp => \"sftp\",\n\t\t\tSelf::Custom(s) => s,\n\t\t}\n\t}\n}\n\nimpl FromLua for Scheme {\n\tfn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {\n\t\tOk(LuaString::from_lua(value, lua)?.to_str()?.parse()?)\n\t}","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/sxyazi/yazi/blob/8ebf930f1796ae2076b7d6b0c5e93a1002c18134/yazi-shared/src/auth/scheme.rs#L35-L71","documentation":"`Scheme::from_str` parses a URL scheme used in Yazi configuration: it accepts the built-ins `regular`, `search`, `sftp` and otherwise a `Custom(KebabCasedKey)` — a 1-20 character kebab-case identifier. Any other string (wrong case, over 20 chars, non-kebab characters like underscores or slashes) is rejected with this error.","triggerScenarios":"Passing a scheme string with uppercase letters, underscores, spaces, a length > 20, or other non-kebab-case characters into any API/config that parses a Scheme (e.g. custom scheme keys in yazi.toml-related parsing, `Scheme::from_str`/DeserializeFromStr).","commonSituations":"Typing `MyScheme` or `my_scheme` in config instead of `my-scheme`; pasting a full URL (`sftp://host`) where only the scheme name is expected; scheme names longer than 20 characters.","solutions":["Rewrite the scheme in lowercase kebab-case, 1-20 characters, e.g. `my-scheme`","Use a built-in scheme name (`regular`, `search`, `sftp`) if that is what you meant","If it came from a URL, extract only the scheme portion before the `://`"],"exampleFix":"// before\nlet s: Scheme = \"Sftp_Plugin\".parse()?;\n// after\nlet s: Scheme = \"sftp-plugin\".parse()?;","handlingStrategy":"validation","validationCode":"fn is_valid_scheme(s: &str) -> bool {\n    matches!(s, \"regular\" | \"search\" | \"sftp\")\n        || (1..=20).contains(&s.len())\n        && s.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')\n        && !s.starts_with('-') && !s.ends_with('-')\n}","typeGuard":"fn try_scheme(s: &str) -> Option<Scheme> { Scheme::from_str(s).ok() }","tryCatchPattern":"let scheme = Scheme::from_str(input)\n    .with_context(|| format(\"bad scheme {input:?}\"))?;","preventionTips":["Write schemes as lowercase kebab-case, 1-20 characters","Never pass a full URL where a scheme name is expected","Avoid underscores and uppercase in custom scheme names"],"tags":["parsing","naming","kebab-case","validation"],"backgroundTag":"invalid-scheme-name-format","analyzedSha":"8ebf930f1796ae2076b7d6b0c5e93a1002c18134","analyzedAt":"2026-09-09T22:26:16.379Z","contentChangedAt":"2026-09-09T22:26:16.379Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}