{"record":{"id":"263ea6f2285429d4","repo":"jdx/mise","slug":"packslip-stampers-entry-spec-has-no-pin-write","errorCode":null,"errorMessage":"packslip.stampers entry {spec:?} has no pin; write host=PUBKEY, host=path/to/key.pub, or host=https://github.com/org/repo/","messagePattern":"packslip\\.stampers entry (.+?) has no pin; write host=PUBKEY, host=path/to/key\\.pub, or host=https://github\\.com/org/repo/","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/packslip_stamps.rs","lineNumber":49,"sourceCode":"use crate::toolset::ToolVersionOptions;\n\n/// GitHub's OIDC issuer, for a stamper pinned by a workflow identity.\nconst GITHUB_ISSUER: &str = \"https://token.actions.githubusercontent.com\";\n\n/// A host whose stamps mise trusts, and how its lists are pinned.\npub(crate) struct Stamper {\n    pub(crate) host: String,\n    pin: Pin,\n}\n\nimpl Stamper {\n    /// `host=PIN`, where PIN is a minisign public key line, the path of a\n    /// `.pub` file, or an `https://` identity prefix for a keyless signer\n    /// on GitHub such as `https://github.com/org/registry/`.\n    pub(crate) fn parse(spec: &str) -> Result<Stamper> {\n        let spec = spec.trim();\n        let Some((host, pin)) = spec.split_once('=') else {\n            bail!(\n                \"packslip.stampers entry {spec:?} has no pin; write host=PUBKEY, host=path/to/key.pub, or host=https://github.com/org/repo/\"\n            );\n        };\n        let host = host.trim();\n        let pin = pin.trim();\n        if host.is_empty() || !host.contains('.') || host.contains('/') {\n            bail!(\"packslip.stampers entry {spec:?}: {host:?} is not a host name\");\n        }\n        let pin = if pin.starts_with(\"https://\") {\n            Pin::Identity(Policy {\n                issuer: Some(GITHUB_ISSUER.into()),\n                identity: None,\n                identity_prefix: Some(pin.to_string()),\n            })\n        } else {\n            let text = if std::path::Path::new(pin).is_file() {\n                file::read_to_string(pin)?\n            } else {","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/packslip_stamps.rs#L31-L67","documentation":"Each entry in the packslip.stampers config must have the form host=PIN, where PIN is a minisign public key, a path to a .pub file, or an https:// identity prefix. This error fires when an entry has no '=' separating host from pin.","triggerScenarios":"Parsing a packslip.stampers config entry that contains no '=' character, e.g. a bare host name or a key pasted without the host= prefix.","commonSituations":"Typos in mise.toml/config stampers section; copying just a public key line without the host= prefix; writing host and pin on separate lines.","solutions":["Add the host= prefix and '=' to the stampers entry","Use the documented forms: host=PUBKEY, host=path/to/key.pub, or host=https://github.com/org/repo/"],"exampleFix":"// before\n[packslip.stampers]\n\"registry.example.com\"\n// after\n[packslip.stampers]\n\"registry.example.com=RWTvK7...base64key\"","handlingStrategy":"validation","validationCode":"if !spec.contains('=') {\n    return Err(format!(\"stampers entry {spec:?} needs host=PIN form\"));\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"has no pin\") => eprintln!(\"fix [packslip.stampers] entry to host=PIN\"),\n    other => other?,\n}","preventionTips":["Always write stampers entries as host=PIN on one line","Copy the full 'host=...' form from docs, not just the key","Validate mise.toml after hand-editing the stampers section","Use a .pub path or https:// identity instead of inlining keys when unsure"],"tags":["configuration","packslip","stampers","format"],"backgroundTag":"invalid-argument-format","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}