{"record":{"id":"b2b18e6f1b16021b","repo":"can1357/oh-my-pi","slug":"invalid-template-with-tmpdir-it-may-not-be","errorCode":null,"errorMessage":"invalid template, {}; with --tmpdir, it may not be absolute","messagePattern":"invalid template, (.+?); with --tmpdir, it may not be absolute","errorType":"error_code","errorClass":"MkTempError","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/mktemp.rs","lineNumber":69,"sourceCode":"\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)]\n\tIo(#[from] io::Error),\n}\n\n/// Options parsed from the command line.\n///\n/// This provides a layer of indirection between the application logic and\n/// `clap`, allowing each to vary independently.\n#[derive(Clone)]\nstruct Options {","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/mktemp.rs#L51-L87","documentation":"MkTempError::InvalidTemplate is raised when the mktemp template is an absolute path but --tmpdir was also given. The two are contradictory: an absolute template already fully specifies the location, while --tmpdir would relocate it, so the tool refuses the ambiguous combination.","triggerScenarios":"`mktemp --tmpdir=/var/tmp /tmp/fooXXXXXX` — absolute template combined with --tmpdir; scripts that unconditionally add --tmpdir while also passing a templated absolute path from a variable.","commonSituations":"Wrapper scripts always injecting --tmpdir \"$TMPDIR\" while callers pass absolute templates; config-driven tmp paths where the template already includes the directory; migration from `mktemp -p` habits.","solutions":["Drop --tmpdir when the template is absolute (it already encodes the directory)","Convert the template to a bare filename and pass the directory via --tmpdir","In wrappers, detect absolute templates and omit the injected --tmpdir: only add --tmpdir when the template is relative","Validate inputs before composing the command: if template starts with '/', skip the flag"],"exampleFix":"// before\nmktemp --tmpdir=/var/tmp /tmp/fooXXXXXX\n// after\nmktemp --tmpdir=/var/tmp fooXXXXXX   // or\nmktemp /tmp/fooXXXXXX","handlingStrategy":"validation","validationCode":"const isAbsoluteTemplate = (t: string): boolean => path.isAbsolute(t);\nif (tmpdirOpt && isAbsoluteTemplate(template)) {\n  throw new RangeError(`template '${template}' is absolute; drop --tmpdir or use a bare filename`);\n}","typeGuard":"const canCombineWithTmpdir = (t: string): boolean => !path.isAbsolute(t);","tryCatchPattern":"try {\n  await Bun.$`mktemp --tmpdir=${dir} ${tmpl}`.quiet();\n} catch (e) {\n  if (String(e).includes('may not be absolute')) {\n    // absolute template already carries its directory\n    await Bun.$`mktemp ${tmpl}`.quiet();\n  } else throw e;\n}","preventionTips":["In wrappers, conditionally add --tmpdir only for relative templates","Choose one source of truth for the temp directory: the template or the flag","Validate template shape before composing the command","Document the constraint wherever templates are user-configurable"],"tags":["cli","argument-validation","mktemp","path"],"backgroundTag":"conflicting-options","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}