Knowledge base

reject-invalid-virtual-mta

<source>

Whether to reject a message that named a nonexistent or empty stream
in the configuration this is a linereject-invalid-virtual-mta trueinside the block <source>
What to do with a message that named a stream that does not exist

The default is true, that is, reject at submission. The sender gets a refusal right away, the message does not reach the node, and this is honest behavior: the stream name is wrong, and the engine does not try to guess.

The second role of this line is more important than the first. The node-level invalid-virtual-mta-fallback directive can send such messages through a fallback stream instead of refusing them. But it does not work while the default stays here: the refusal fires first. Two lines in two different scopes describe one behavior, and both have to be configured.

The main trap. Writing only the fallback stream and deciding the job is done. The configuration looks right, is applied without warnings and changes nothing — messages are still rejected. The reverse is just as true: turning off the refusal without naming a fallback stream leaves you with neither behavior.
What the Hub does. It does not write it and leaves the vendor default — that is, it rejects. For us this is right: the Hub sets stream names itself, and a message with an unknown name means a mismatch between the plan and the node, which is better learned from a refusal than from quietly sending from the wrong address. The “fallback stream plus refusal turned off” pair will come in handy where headers are set by someone else’s platform whose updates we do not control.
Recommended values

What to set. The directive makes sense only paired with invalid-virtual-mta-fallback, and there are two meaningful combinations:

Here Fallback stream What happens
true (default) not set refusal at submission — the sender learns right away
false set the message leaves through the fallback stream
false not set the behavior is not described — do not configure it this way
true set the fallback stream has no effect, the refusal remains

The first suits your own submission: the refusal is visible and gets fixed. The second suits someone else’s, where the refusal gets lost in other people’s logs and the message must not be lost.

The true default and the requirement to turn it off for the fallback stream come from the manual. The breakdown by combinations is ours.
Example configuration
# both lines are needed together: without this one the fallback stream has no effect
invalid-virtual-mta-fallback mta-hub-pool

<source 10.0.0.0/24>
    process-x-virtual-mta yes
    reject-invalid-virtual-mta false</source>
The first line is node-level, the second source-level. A message from this subnet with a header naming a nonexistent stream will leave through the pool instead of being refused. Remove either of the two — and the refusal comes back.
yellow — — directive names, blue — blocks. They are highlighted the same way in the text of the card.
▲

Common mistakes and problems

what people get wrong and how it ends3 mistakes▶

Setting a fallback stream while leaving the refusal on

§ 3.3.1.55.2

What it costsThe fallback stream will have no effect: the true default fires first. The configuration looks configured, is applied without warnings and works the old way.
How to spot itThe platform keeps getting submission refusals, even though the fallback stream is in the configuration and its name is correct.
How to do it rightAdd reject-invalid-virtual-mta false to the source the submission comes from. The rule is simple: two lines, both mandatory.

Turning off the refusal without naming a fallback stream

§ 3.3.1.55.2

What it costsThe manual describes turning off the refusal only together with a fallback stream; the behavior without one is not documented. The refusal is removed, and where the message will go is unknown.
How to spot itSome messages are neither rejected nor found in the queues; accounting has nothing on them.
How to do it rightEither name a fallback stream or restore the default. Half of this pair is not a configuration.

Turning off the refusal for the whole node for the sake of one platform

our experience

What it costsA line in the wide <source> 0/0 block removes the refusal for everyone, including your own submission. A typo in a stream name in a warm-up plan stops being a visible error and turns into messages sent from the wrong address.
How to spot itWarm-up is “running,” but the daily volumes per address do not match the plan.
How to do it rightTurn off the refusal only in a narrow block — the one for the subnet used by the platform all this is being done for.
Compatibility
PowerMTA 6.0
described in the manual of this version · platform: any
The vendor does not state the first version for every directive — there is no note here. The name also appears in the 5.0 edition of the manual.
Reference data
Syntax
reject-invalid-virtual-mta <boolean>
Values
yes | no | true | false
Default
true § 3.3.7.34
Applying
reload, no restart needed
Queue
will not touch what is already queued
Where to write it
<source>
How many times
once in the block
Everything here comes from the manual, with the section.
Inheritance
The directive lives in <source> and merges by the source rule: a narrower block overrides a wider one. Hence a useful consequence: the refusal can be left at the default for everyone and turned off pointwise — in the block for the subnet from which someone else’s platform submits.
How to check on the node
Check the pair, not one line. Both are visible in the assembled configuration:
grep -n "invalid-virtual-mta" /etc/pmta/config
The output must contain both lines: invalid-virtual-mta-fallback at the root of the file and reject-invalid-virtual-mta false in the right source. One without the other means that what is configured is not what was intended.
In the Hub
Cost of a mistake
reversible: harms only while it is set
Scenarios
Sending address rotation · Connect a sending platform