{"record":{"id":"581d1c58d6da5c55","repo":"can1357/oh-my-pi","slug":"too-few-x-s-in-template","errorCode":null,"errorMessage":"too few X's in template {}","messagePattern":"too few X's in template (.+?)","errorType":"error_code","errorClass":"MkTempError","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/mktemp.rs","lineNumber":60,"sourceCode":"\nstatic ARG_TEMPLATE: &str = \"template\";\n\n#[cfg(not(windows))]\nconst TMPDIR_ENV_VAR: &str = \"TMPDIR\";\n#[cfg(windows)]\nconst TMPDIR_ENV_VAR: &str = \"TMP\";\n\nconst FALLBACK_TMPDIR: &str = \"/tmp\";\n\n#[derive(Error, Debug)]\nenum MkTempError {\n\t#[error(\"could not persist file {}\", .0.quote())]\n\tPersist(PathBuf),\n\n\t#[error(\"with --suffix, template {} must end in X\", .0.quote())]\n\tMustEndInX(String),\n\n\t#[error(\"too few X's in template {}\", .0.quote())]\n\tTooFewXs(String),\n\n\t#[error(\"invalid template, {}, contains directory separator\", .0.quote())]\n\tPrefixContainsDirSeparator(String),\n\n\t#[error(\"invalid suffix {}, contains directory separator\", .0.quote())]\n\tSuffixContainsDirSeparator(String),\n\n\t#[error(\"invalid template, {}; with --tmpdir, it may not be absolute\", .0.quote())]\n\tInvalidTemplate(OsString),\n\n\t#[error(\"too many templates\")]\n\tTooManyTemplates,\n\n\t#[error(\"failed to create {} via template {}: No such file or directory\", .0, .1.quote())]\n\tNotFound(String, PathBuf),\n\n\t#[error(transparent)]","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/mktemp.rs#L42-L78","documentation":"MkTempError::TooFewXs is raised when the mktemp template contains fewer than the required number of trailing X characters for the random-part replacement (GNU mktemp requires at least 3 X's). The template's trailing X-run is replaced with randomness; too few X's would yield insufficient entropy, so the tool refuses.","triggerScenarios":"`mktemp tmp` or `mktemp fooX` — templates with fewer than 3 trailing X's; programmatically built templates where a loop generated only 1–2 X's.","commonSituations":"Hand-written templates like `mktemp file.XX`; code that strips or truncates X's by accident; porting from tools that accept fewer X's and pad internally (GNU does not).","solutions":["Use at least 3 trailing X's, e.g. `mktemp XXXXXX` (more X's = more entropy)","Generate templates programmatically with a fixed X-run of sufficient length","If only one random file is needed, more X's are still cheap — use 6+ for safety","Avoid tools/internal helpers that pass user input through as the template without validating X count"],"exampleFix":"// before\nmktemp tmpXX\n// after\nmktemp tmpXXXXXX","handlingStrategy":"validation","validationCode":"const xs = (t: string): number => (t.match(/X+$/) ?? [''])[0].length;\nif (xs(template) < 3) {\n  throw new RangeError(`too few X's in template '${template}': need at least 3 trailing X's`);\n}","typeGuard":"const hasEnoughXs = (t: string): boolean => (t.match(/X+$/) ?? [''])[0].length >= 3;","tryCatchPattern":"try {\n  await Bun.$`mktemp ${tmpl}`.quiet();\n} catch (e) {\n  if (String(e).includes(\"too few X's\")) {\n    await Bun.$`mktemp ${tmpl.replace(/X*$/, 'XXXXXX')}`.quiet(); // pad to 6 X's\n  } else throw e;\n}","preventionTips":["Standardize on 6+ trailing X's in all templates","Lint templates passed to mktemp for X-count","Never build templates from user input without an X-run check","Prefer mktemp defaults (no template) when a fixed pattern isn't required"],"tags":["cli","argument-validation","mktemp"],"backgroundTag":"invalid-template-argument","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}