{"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":"validation","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/533346cc374382b41ec5ff70536252b2e96e725c/src/packslip_stamps.rs#L31-L67","documentation":"Raised by packslip_stamps::Stamper::parse when a `packslip.stampers` entry does not contain an '=' separator. A stamper entry must be written as `host=PIN`, where the pin is either a minisign public key line, a path to a .pub file, or an https:// GitHub identity prefix. Without '=', no host or pin can be extracted.","triggerScenarios":"Parsing a stampers config entry (e.g. `packslip.stampers = [\"github.com\"]` or a value with no '=' at all) via Stamper::parse — the split_once('=') returns None.","commonSituations":"Writing only the host name in mise.toml and forgetting the '=pubkey' part; pasting a bare public key without prefixing the host; quoting or whitespace mistakes that drop the '='.","solutions":["Rewrite the entry as host=PIN: e.g. \"example.com=path/to/key.pub\" or \"example.com=<minisign public key line>\".","For a keyless GitHub signer, use the identity-prefix form, e.g. \"example.com=https://github.com/org/repo/\".","Check for typos — the '=' must be present even if the pin is a path or URL."],"exampleFix":"// before (mise.toml)\n[packslip]\nstampers = [\"example.com\"]\n// after\n[packslip]\nstampers = [\"example.com=https://github.com/org/repo/\"]","handlingStrategy":"validation","validationCode":"// validate stamper entries before writing them to config\nfunction isValidStamperEntry(spec) {\n  return typeof spec === \"string\" && spec.includes(\"=\") && spec.trim().length > 0;\n}","typeGuard":"function isHostPinPair(spec) {\n  if (typeof spec !== \"string\") return false;\n  const i = spec.indexOf(\"=\");\n  return i > 0 && i < spec.length - 1;\n}","tryCatchPattern":"try {\n  parseStamper(spec);\n} catch (e) {\n  if (String(e).includes(\"has no pin\")) {\n    throw new Error(`Fix ${spec}: use host=PUBKEY, host=path/to/key.pub, or host=https://github.com/org/repo/`);\n  } else throw e;\n}","preventionTips":["Always write stamper entries in host=pin form; the '=' is mandatory.","Copy entry syntax from the packslip docs rather than composing from memory.","Lint mise.toml stampers arrays in CI before merging config changes."],"tags":["packslip","configuration","parsing","stamper"],"backgroundTag":"missing-required-argument","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}