{"record":{"id":"748bdcf4f932737c","repo":"denisidoro/navi","slug":"given-options-are-missing-a-closing-quote","errorCode":null,"errorMessage":"Given options are missing a closing quote","messagePattern":"Given options are missing a closing quote","errorType":"validation","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"src/parser.rs","lineNumber":19,"sourceCode":"use crate::common::fs;\nuse crate::deser;\nuse crate::finder::structures::{Opts as FinderOpts, SuggestionType};\nuse crate::prelude::*;\nuse crate::structures::cheat::VariableMap;\nuse crate::structures::item::Item;\nuse std::io::Write;\n\nlazy_static! {\n    pub static ref VAR_LINE_REGEX: Regex = Regex::new(r\"^\\$\\s*([^:]+):(.*)\").expect(\"Invalid regex\");\n}\n\nfn parse_opts(text: &str) -> Result<FinderOpts> {\n    let mut multi = false;\n    let mut prevent_extra = false;\n\n    let mut opts = FinderOpts::var_default();\n\n    let parts = shellwords::split(text).map_err(|_| anyhow!(\"Given options are missing a closing quote\"))?;\n\n    parts\n        .into_iter()\n        .filter(|part| {\n            // We'll take parts in pairs of 2: (argument, value). Flags don't have a value tho, so we filter and handle them beforehand.\n            match part.as_str() {\n                \"--multi\" => {\n                    multi = true;\n                    false\n                }\n                \"--prevent-extra\" => {\n                    prevent_extra = true;\n                    false\n                }\n                \"--expand\" => {\n                    opts.map = Some(format!(\"{} fn map::expand\", fs::exe_string()));\n                    false\n                }","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/denisidoro/navi/blob/f7330b9ad5bd95b7d1a3c96d00e0a77deb589147/src/parser.rs#L1-L37","documentation":"Thrown by `parse_opts` in src/parser.rs when `shellwords::split(text)` fails, which happens when the finder options string contains an unterminated quote. The library treats the whole options text as shell-like words, so unbalanced quoting is unrecoverable.","triggerScenarios":"A snippet/config line's options section after `---` contains a quote character that is opened but never closed, e.g. `--- --header \"my title` or `--fzf-overrides '--preview 'foo'` with mismatched quotes.","commonSituations":"Hand-writing fzf overrides in a config file with nested quotes; copy-pasting shell commands with smart/curly quotes; editing snippet definitions and deleting a closing quote.","solutions":["Add the missing closing quote to the options string in the config/snippet line","Remove quotes entirely if the option value contains no spaces","Replace smart quotes (“ ” ‘ ’) with plain ASCII quotes","Test the options string in a real shell first; if the shell rejects it, the parser will too"],"exampleFix":"// before (config line)\nmy-var: cat file.txt --- --header \"my title\n// after\nmy-var: cat file.txt --- --header \"my title\"","handlingStrategy":"validation","validationCode":"fn quotes_balanced(s: &str) -> bool {\n    let mut n = 0;\n    for c in s.chars() {\n        if c == '\"' || c == '\\'' { n += 1; }\n    }\n    n % 2 == 0\n}\n// check quotes_balanced(options_text) before feeding it to the parser","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Test option strings in a real shell before pasting them into config","Avoid smart quotes from word processors; type plain ASCII quotes","Prefer no quotes when values contain no spaces"],"tags":["parsing","shellwords","config"],"backgroundTag":"unterminated-quote","analyzedSha":"f7330b9ad5bd95b7d1a3c96d00e0a77deb589147","analyzedAt":"2026-09-03T13:58:22.429Z","contentChangedAt":"2026-09-03T13:58:22.429Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}