jdx/mise · error

user service ' ': environment key cannot be set through…

Error message

user service '{}': environment key {key:?} cannot be set through cmd.exe

What it means

When a user service (scheduled task) declares an `environment` map, the values must be injected via a cmd.exe wrapper, and cmd.exe cannot safely set arbitrary environment keys. Keys that are empty or contain '=', quotes, '%', or newlines would break or be reinterpreted by cmd.exe, so exec_action (called by render_xml) rejects them.

Solutions

  1. Sanitize the environment key: remove '=', quotes, '%' and line breaks
  2. Set the variable inside the program/script instead of via request.environment
  3. Wrap the whole env setup in a script file and drop the environment map

Example fix

// before
environment: { "PATH=EXTRA": "/x" }
// after
environment: { "PATH_EXTRA": "/x" }
Defensive patterns

Strategy: validation

Validate before calling

const badKey = Object.keys(svc.environment).find(k => !k || /[="%'\r\n]/.test(k));
if (badKey !== undefined) throw new Error(`env key ${JSON.stringify(badKey)} not cmd.exe-safe`);

Type guard

function isCmdSafeKey(k) { return typeof k === 'string' && k.length > 0 && !/[="%'\r\n]/.test(k); }

Try / catch

try { renderServiceXml(svc); } catch (e) { if (String(e).includes('cannot be set through cmd.exe')) { sanitizeEnvKeys(svc.environment); } else throw e; }

Prevention

When it happens

Trigger: Calling the service/task rendering path (render_xml -> exec_action) on Windows for a service whose request.environment contains a key that is empty or contains '=', '"', '%', '\n', or '\r'.

Common situations: Copying Linux-style env definitions with '=' inside keys or quoted keys; keys built by string interpolation that accidentally include a percent sign; multiline keys pasted from a .env file.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/42dc2e23554ef579. Report an issue: GitHub.

Appendix: source

Thrown at src/system/scheduled_tasks.rs:211

    }
    out.push_str("    </Exec>\n  </Actions>\n");
    out.push_str("</Task>\n");
    Ok(out)
}

/// Split the command line into the executable and its arguments. Task
/// Scheduler has no environment block, so variables are set through
/// `cmd.exe`, which reinterprets some characters; values that it would
/// change are rejected rather than passed through differently.
fn exec_action(request: &ScheduledTaskRequest) -> Result<(String, String)> {
    let (program, args) = split_command(&request.command);
    if request.environment.is_empty() {
        return Ok((program, args));
    }
    let mut sets = vec![];
    for (key, value) in &request.environment {
        if key.is_empty() || key.contains(['=', '"', '%', '\n', '\r']) {
            bail!(
                "user service '{}': environment key {key:?} cannot be set through cmd.exe",
                request.name
            );
        }
        if let Some(c) = value
            .chars()
            .find(|c| matches!(c, '"' | '%' | '&' | '|' | '<' | '>' | '^' | '\n' | '\r'))
        {
            bail!(
                "user service '{}': environment value for {key} contains {c:?}, which cmd.exe would reinterpret; set it inside the program instead",
                request.name
            );
        }
        sets.push(format!("set \"{key}={value}\""));
    }
    // the command line goes through cmd.exe too: what it would split or
    // chain is rejected the same way, rather than run differently
    if let Some(c) = format!("{program} {args}")

View on GitHub (pinned to 533346cc37)