Fix it now
1639 is ERROR_INVALID_COMMAND_LINE – msiexec could not parse what it was given – and 1624 is ERROR_INSTALL_TRANSFORM_FAILURE, an error applying transforms, with Microsoft asking you to verify the transform paths are valid. Both are deployment faults rather than package faults: the same MSI that fails from a script installs perfectly when you double-click it.
msiexec /i "C:\pkg\app.msi" /qn /norestart TRANSFORMS="C:\pkg\site.mst;C:\pkg\lang.mst" INSTALLDIR="C:\Program Files\App" ALLUSERS=1 /l*v "C:\pkg\install.log"
- Print the exact command your script builds immediately before it runs, and read it. Most 1639 faults are visible the moment you see the expanded string.
- Quote the value, not the assignment.
INSTALLDIR="C:\Program Files\App"parses;"INSTALLDIR=C:\Program Files\App"does not. - Write public property names in capitals. Microsoft’s rule is that public property names cannot contain lowercase letters, and anything else is private and never reaches the part of the install that changes the machine.
- For 1624, confirm each transform exists at that exact path, is readable by the account running the install, and was generated against this package.
- In PowerShell, build the arguments as an array and use
Start-Process msiexec -ArgumentList $args -Wait, or put--%ahead of the arguments so PowerShell stops interpreting them.
Do not mix bare file names and full paths in one TRANSFORMS list. Microsoft states plainly that you cannot use file names and paths together in the same TRANSFORMS list, and a mixed list is a common source of 1624.
If a log file appears at the path you named, the command line parsed and you are past 1639. If the transforms still fail, the next section explains what a transform validates against.
Why it happens
Windows Installer parses its own command line rather than relying on the shell, and the grammar is stricter than most command-line tools. Switches come first, then the package, then any number of property assignments in the form NAME=value. Anything the parser cannot fit into that shape produces 1639 without attempting the install – which is why the error arrives instantly and no log is written unless the logging switch itself parsed correctly.
Spaces are where this usually breaks. A path containing spaces has to be quoted, and the quotation marks must enclose only the value. Quote the whole assignment and the parser sees one long token that is not a valid property name. The same applies to the log path after /l*v, which is a separate argument and needs its own quotes. And the case rule catches people who never see an error at all: Microsoft documents that public property names cannot contain lowercase letters, and that properties set during the user interface phase and passed to the execution phase must be public – so a lowercase property parses cleanly, does nothing, and the install succeeds while ignoring you.
Transforms are passed as a property rather than a switch, and they carry more rules than most people realise. TRANSFORMS takes a semicolon-separated list applied in the order given. A leading colon means the transform is embedded in a storage inside the package rather than being a file on disk, and Microsoft notes that embedded transforms are not cached and are always obtained from the package. A leading vertical bar marks a secure-full-path transform, where the source must be at the full path passed to TRANSFORMS. And you cannot mix bare file names with full paths in the same list at all.
Quoting or spacing in the generated command
You have this one if 1639 returns immediately, no log file is created, and the command was built by concatenating strings in a script.
- Echo the fully expanded command to your own log before executing it, and paste that line into a Command Prompt by hand to confirm it parses.
- Quote each path individually: the package, each transform and the log target.
- Strip trailing spaces and line-continuation characters left by the script’s formatting.
- Rebuild the command with an argument array rather than string concatenation wherever the language supports it.
The transform path is not valid in the install context
You have this one if 1624, with the transform sitting on a network share or a mapped drive while the install runs from a deployment tool.
- Copy the package and every transform to a local staging folder as part of the deployment, and reference the local copies.
- Never use mapped drive letters; they do not exist for the account the install runs under.
- Where a UNC path is unavoidable, grant read access to the computer accounts as well as to users.
- Retry and confirm the log records each transform being applied in the order you listed.
File names and paths mixed in one TRANSFORMS list
You have this one if 1624 on a list where some entries are bare names and others are full paths, often after someone added one transform to an existing command.
- Pick one form for the whole list. Full paths are the safer choice for deployment.
- Where a transform lives inside the package, prefix it with a colon and remember that embedded transforms are never cached.
- Where you need the secure-full-path behaviour, prefix with a vertical bar and make sure each transform really is at the full path you pass.
- Retest the whole list rather than the entry you changed.
The transform was generated against a different package
You have this one if 1624 with the transform present locally and readable, on a package the vendor has since updated.
- Regenerate the transform against the exact package you are deploying now.
- Where the transform comes from a vendor, ask for the version matching your package build.
- If you author transforms in-house, version the base package and the transform together so they are never separated.
- Test the pair on a clean machine before wide deployment.
Transforms record validation rules when they are built. A transform that was valid for last quarter’s package can be rejected by this quarter’s, and that rejection is correct behaviour rather than a bug.
Policy is refusing the customisation
You have this one if 1644 rather than 1624, on machines with application control or software restriction policies applied.
- Read Microsoft’s wording for 1644: one or more customisations are not permitted by system policy. That is a policy decision, not a file problem.
- Produce a report with
gpresult /h %USERPROFILE%\Desktop\gp.htmland read the Windows Installer and application control sections. - Check the Windows Installer machine policies under
HKLM\Software\Policies\Microsoft\Windows\Installer, including TransformsSecure, which requires transforms to be cached where the user cannot write. - Move the transform into a location the policy trusts, or have the policy amended, then run
gpupdate /forceon a test machine and retry.
Full reference
Command lines that work and command lines that do not
| Written as | Result |
|---|---|
msiexec /i "C:\pkg\app.msi" /qn |
Correct: switch, quoted package path, quiet |
msiexec /i C:\Program Files\app.msi /qn |
Fails: an unquoted path with a space is parsed as several arguments |
INSTALLDIR="C:\Program Files\App" |
Correct: only the value is quoted |
"INSTALLDIR=C:\Program Files\App" |
Fails with 1639: the whole token is not a valid assignment |
TRANSFORMS="C:\pkg\a.mst;C:\pkg\b.mst" |
Correct: semicolon-separated full paths, applied in order |
TRANSFORMS="a.mst;C:\pkg\b.mst" |
Fails: file names and paths cannot be mixed in one list |
installdir=C:\Apps |
Parses, and does nothing: a lowercase name is private and never reaches the execution phase |
msiexec /i "C:\pkg\app.msi" ^
TRANSFORMS="C:\pkg\site.mst;C:\pkg\lang.mst" ^
INSTALLDIR="C:\Program Files\App" ALLUSERS=1 ^
/qn /norestart /l*v "C:\pkg\install.log"
Transform prefixes, and what each means
| Written as | Meaning |
|---|---|
a.mst |
A file name; treated as secure-at-source or unsecured depending on policy |
C:\pkg\a.mst |
A full path; treated as secure-full-path or unsecured depending on policy |
:a.mst |
Embedded in a storage inside the package; never cached, always read from the package |
|C:\pkg\a.mst |
Secure-full-path: the source must be at the full path passed to TRANSFORMS |
The logging switch, and why it matters here
/l*v is worth adding to every unattended command line, not only when troubleshooting, because its presence or absence is itself a diagnostic. Microsoft documents that the path to the log file must already exist – the installer will not create the directory structure – so a missing log can mean either that the command line failed to parse or that the folder is not there. Create the staging folder as the first step of your deployment and that ambiguity disappears.
Internal errors you may see alongside 1624
| Code | Message |
|---|---|
| 2226 | Database: [2]. Transform failed |
| 2247 | Database: [2] Transform stream read/write failure |
Both are internal messages rather than exit codes, and both point at the transform rather than at your command line. 2226 is the general statement that applying the transform failed; 2247 is more specific, a read or write failure against the transform stream itself, which is what a truncated or corrupted .mst produces. Regenerate the transform rather than editing it.
Making unattended deployment repeatable
- Stage the package and every transform to a local folder as the first step, and reference them by absolute local path.
- Build the argument list as an array, not by string concatenation, and log the expanded command before you run it.
- Keep property names in capitals, and check the log to confirm the values you passed appear with the values you meant.
- Version the transform with the package it was built against, and regenerate rather than reuse when the vendor ships a new build.
- Treat 1639 as a script defect and 1624 as a file or policy defect. They look similar in a deployment report and they are not.
Every code this article covers
| Code | What it points at | Source |
|---|---|---|
1639 |
ERROR_INVALID_COMMAND_LINE: the command line passed to the installer could not be parsed | Microsoft Learn |
1624 |
ERROR_INSTALL_TRANSFORM_FAILURE: there was an error applying transforms; verify that the specified transform paths are valid | Microsoft Learn |
1644 |
ERROR_INSTALL_TRANSFORM_REJECTED: one or more customisations are not permitted by system policy | Microsoft Learn |
2226 |
Database: [2]. Transform failed – an internal message reporting that applying the transform did not succeed | Microsoft Learn |
2247 |
Database: [2] Transform stream read/write failure – a read or write against the transform stream itself failed | Microsoft Learn |
Confirm the fix worked
- A log file appears at the path you specified, which proves the command line parsed.
- The log records each transform being applied, in the order you listed them.
- The properties you passed appear in the log with the values you intended, in capitals.
- The same command runs through the deployment tool, not only from a Command Prompt.
- A second machine of the same build produces the same result from the staged copies.
Questions people ask about this
Is there anything to buy to fix this?
No. This is command-line syntax and file access. Every fix is a change to the script or to where you stage the files.
Why does the install work interactively and fail from my deployment tool?
Different account, different session, no mapped drives, no user profile. Stage the package and transforms locally and reference them by absolute local path, and the difference goes away.
How do I apply more than one transform?
List them in the TRANSFORMS property separated by semicolons, in the order you want them applied, and quote the whole list. Use full paths for all of them – Microsoft states you cannot mix file names and paths in the same list. A transform stored inside the package is referenced with a leading colon.
Why does my property have no effect even though the install succeeds?
Almost certainly because it is written in lower or mixed case. Microsoft’s rule is that public property names cannot contain lowercase letters, and only public properties are passed from the user interface phase to the execution phase. Anything else is private and ignored.
I get 1644 rather than 1624. Same fix?
No. 1644 means one or more customisations are not permitted by system policy – the transform was understood and refused. Look at the Windows Installer policies and any application control policy applied to the machine, and change the policy rather than the file.
