How to Validate Barcode Checksums and Use Format-Aware Reading in C#
Barcode checksums help detect substitution errors. For example, a single transposed digit in an EAN-13 label can send a package to the wrong warehouse. Format-aware reading provides an additional validation layer by restricting the decoder to expected symbologies. This approach reduces false positives from background noise and shortens scan time by skipping unnecessary format detectors.
IronBarcode performs checksum verification automatically during decoding. Each symbology's check-digit algorithm runs by default, and barcodes that fail are discarded before results are returned. The BarcodeReaderOptions.ExpectBarcodeTypes property limits reads to specific formats, while RemoveFalsePositive adds a secondary scan for ambiguous reads.
This guide explains how to validate barcode checksums, constrain reads to expected formats, and combine both techniques into a layered quality gate using BarcodeReaderOptions.
Quickstart: Validate Barcodes with Checksum and Format Constraints
Configure BarcodeReaderOptions with ExpectBarcodeTypes and RemoveFalsePositive to constrain reads to expected symbologies with automatic checksum verification.
:path=/static-assets/barcode/content-code-examples/how-to/checksum-and-format-validation/quickstart.cs
using IronBarCode;
// Format-constrained read with false-positive removal.
// Limit the decoder to EAN-13 and Code128; checksums are
// validated automatically and failures are silently discarded.
var options = new BarcodeReaderOptions
{
ExpectBarcodeTypes = BarcodeEncoding.EAN13 | BarcodeEncoding.Code128,
RemoveFalsePositive = true,
Speed = ReadingSpeed.Balanced
};
BarcodeResults results = BarcodeReader.Read("label.png", options);
Imports IronBarCode
' Format-constrained read with false-positive removal.
' Limit the decoder to EAN-13 and Code128; checksums are
' validated automatically and failures are silently discarded.
Dim options As New BarcodeReaderOptions With {
.ExpectBarcodeTypes = BarcodeEncoding.EAN13 Or BarcodeEncoding.Code128,
.RemoveFalsePositive = True,
.Speed = ReadingSpeed.Balanced
}
Dim results As BarcodeResults = BarcodeReader.Read("label.png", options)
Minimal Workflow (5 steps)
- Download the IronBarcode library from NuGet
- Create a
BarcodeReaderOptionsinstance - Set
ExpectBarcodeTypesto the symbologies present in the pipeline - Enable
RemoveFalsePositivefor secondary verification - Call
BarcodeReader.Readto decode, checksums are validated automatically during decode
How to Validate Barcode Checksums?
IronBarcode validates checksums during decoding according to each symbology's specification. For example, when reading an EAN-13 barcode, the Mod10 check digit is calculated from the first 12 digits and compared to the 13th. If the digits do not match, the barcode is silently rejected and does not appear in the BarcodeResults collection. This approach applies to all formats with a mandatory check digit, including UPC-A, UPC-E, EAN-8, Code128, ITF, and others.
This implicit model differs from libraries that expose an explicit toggle. The table below compares the two approaches:
| Aspect | IronBarcode | Aspose.BarCode |
|---|---|---|
| Validation trigger | Automatic; runs during every decode | Explicit: ChecksumValidation.On / Off / Default |
| Developer action required | None; invalid barcodes are excluded from results | Set BarcodeSettings.ChecksumValidation before reading |
| Checksum disable | Not exposed; checksums are always enforced for mandatory formats | Yes; ChecksumValidation.Off skips verification |
| Optional-checksum formats (Code39) | Uses Confidence + RemoveFalsePositive to filter low-quality reads | Explicitly enable with EnableChecksum.Yes |
| Failure behavior | Barcode silently omitted from results | Barcode may appear with a separate checksum value for manual inspection |
For symbologies with optional checksums, such as Code39, the library uses confidence scoring and RemoveFalsePositive instead of a checksum toggle.
Input
A Code128 warehouse rack label (success path) and a blank image with no barcode (failure path).
warehouse-rack.png (success path)
blank-no-barcode.png (failure path — no barcode present)
:path=/static-assets/barcode/content-code-examples/how-to/checksum-and-format-validation/checksum-confidence.cs
using IronBarCode;
// Constrain reads to 1D formats and enable secondary verification.
// ConfidenceThreshold rejects decodes where the ML detector falls below 85%,
// acting as a quality gate for optional-checksum symbologies like Code39.
var options = new BarcodeReaderOptions
{
ExpectBarcodeTypes = BarcodeEncoding.AllOneDimensional,
RemoveFalsePositive = true,
ConfidenceThreshold = 0.85,
Speed = ReadingSpeed.Detailed
};
BarcodeResults results = BarcodeReader.Read("warehouse-rack.png", options);
foreach (BarcodeResult result in results)
{
// Each result has passed checksum validation (mandatory formats)
// and the 85% confidence threshold, so no additional filtering is needed.
Console.WriteLine($"[{result.BarcodeType}] {result.Value} page={result.PageNumber}");
}
if (results.Count == 0)
{
Console.Error.WriteLine("No valid barcodes found. Possible causes:");
Console.Error.WriteLine(" - Check digit mismatch (barcode silently rejected)");
Console.Error.WriteLine(" - Confidence below 85% threshold");
Console.Error.WriteLine(" - Format not in ExpectBarcodeTypes");
}
Imports IronBarCode
' Constrain reads to 1D formats and enable secondary verification.
' ConfidenceThreshold rejects decodes where the ML detector falls below 85%,
' acting as a quality gate for optional-checksum symbologies like Code39.
Dim options As New BarcodeReaderOptions With {
.ExpectBarcodeTypes = BarcodeEncoding.AllOneDimensional,
.RemoveFalsePositive = True,
.ConfidenceThreshold = 0.85,
.Speed = ReadingSpeed.Detailed
}
Dim results As BarcodeResults = BarcodeReader.Read("warehouse-rack.png", options)
For Each result As BarcodeResult In results
' Each result has passed checksum validation (mandatory formats)
' and the 85% confidence threshold, so no additional filtering is needed.
Console.WriteLine($"[{result.BarcodeType}] {result.Value} page={result.PageNumber}")
Next
If results.Count = 0 Then
Console.Error.WriteLine("No valid barcodes found. Possible causes:")
Console.Error.WriteLine(" - Check digit mismatch (barcode silently rejected)")
Console.Error.WriteLine(" - Confidence below 85% threshold")
Console.Error.WriteLine(" - Format not in ExpectBarcodeTypes")
End If
Output
Success Path
The warehouse rack barcode came back as RACK-A1-LOT-7382 on page 0. It cleared the 85% confidence threshold and passed checksum validation, so it shows up in BarcodeResults.
Failure Path
Raising ConfidenceThreshold above its 0.7 default tightens this gate further for optional-checksum symbologies such as Code39.
With checksum validation covered, the next step is restricting the reader to the barcode formats your pipeline expects.
How to Use Format-Aware Barcode Reading?
The BarcodeEncoding enum is a flags type, allowing multiple formats to be combined using the bitwise OR operator. Setting ExpectBarcodeTypes limits the reader to those formats and skips detection for others.
| Value | Category | Description | Checksum |
|---|---|---|---|
BarcodeEncoding.All | Meta | All supported formats (default behavior) | Per-format |
BarcodeEncoding.AllOneDimensional | Meta | All linear (1D) formats including stacked | Per-format |
BarcodeEncoding.AllTwoDimensional | Meta | All matrix/grid (2D) formats | Per-format |
BarcodeEncoding.Code128 | 1D | High-density alphanumeric (logistics, shipping) | Mandatory (weighted Mod103) |
BarcodeEncoding.EAN13 | 1D | Retail product identification, 13 digits | Mandatory (Mod10) |
BarcodeEncoding.QRCode | 2D | High-capacity matrix (URLs, structured data) | Reed-Solomon ECC |
BarcodeEncoding.Code39 | 1D | Alphanumeric (defense, automotive) | Optional (Mod43) |
BarcodeEncoding.UPCA | 1D | North American retail, 12 digits | Mandatory (Mod10) |
BarcodeEncoding.DataMatrix | 2D | Compact matrix (electronics, pharma) | Reed-Solomon ECC |
BarcodeEncoding.PDF417 | 2D | Stacked (ID cards, transport) | Reed-Solomon ECC |
Beyond speed, constraining the format set acts as a validation gate: barcodes of any unlisted symbology are excluded from results even if physically present in the image.
Input
A Code128 shipping label (success path) and a QR code that does not match the Code128-only constraint (failure path).
shipping-label.png (success path — Code128 matches constraint)
qr-format-mismatch.png (failure path — QR rejected by Code128-only filter)
:path=/static-assets/barcode/content-code-examples/how-to/checksum-and-format-validation/format-constrained.cs
using IronBarCode;
// Constrained read: only Code128 barcodes are returned.
// Faster because the reader skips all other format detectors.
var constrainedOptions = new BarcodeReaderOptions
{
ExpectBarcodeTypes = BarcodeEncoding.Code128,
Speed = ReadingSpeed.Faster,
ExpectMultipleBarcodes = false
};
// Broad read: all supported formats are scanned.
// Useful for verification or when the image format is unknown.
var broadOptions = new BarcodeReaderOptions
{
ExpectBarcodeTypes = BarcodeEncoding.All,
Speed = ReadingSpeed.Detailed,
ExpectMultipleBarcodes = true
};
string imagePath = "shipping-label.png";
BarcodeResults constrained = BarcodeReader.Read(imagePath, constrainedOptions);
Console.WriteLine($"Constrained: {constrained.Count} Code128 barcode(s) found");
BarcodeResults broad = BarcodeReader.Read(imagePath, broadOptions);
Console.WriteLine($"Broad: {broad.Count} barcode(s) found across all formats");
foreach (BarcodeResult result in broad)
{
Console.WriteLine($" [{result.BarcodeType}] {result.Value}");
}
Imports IronBarCode
' Constrained read: only Code128 barcodes are returned.
' Faster because the reader skips all other format detectors.
Dim constrainedOptions As New BarcodeReaderOptions With {
.ExpectBarcodeTypes = BarcodeEncoding.Code128,
.Speed = ReadingSpeed.Faster,
.ExpectMultipleBarcodes = False
}
' Broad read: all supported formats are scanned.
' Useful for verification or when the image format is unknown.
Dim broadOptions As New BarcodeReaderOptions With {
.ExpectBarcodeTypes = BarcodeEncoding.All,
.Speed = ReadingSpeed.Detailed,
.ExpectMultipleBarcodes = True
}
Dim imagePath As String = "shipping-label.png"
Dim constrained As BarcodeResults = BarcodeReader.Read(imagePath, constrainedOptions)
Console.WriteLine($"Constrained: {constrained.Count} Code128 barcode(s) found")
Dim broad As BarcodeResults = BarcodeReader.Read(imagePath, broadOptions)
Console.WriteLine($"Broad: {broad.Count} barcode(s) found across all formats")
For Each result As BarcodeResult In broad
Console.WriteLine($" [{result.BarcodeType}] {result.Value}")
Next
Output
Success Path
The shipping label has a value of SHIP-2024-00438. The constrained read picks it up straight away since Code128 is what the filter expects, and the broad read confirms the same result across all formats.
Failure Path
Empty results from a constrained read are a validation signal, not an error; log the discrepancy for review.
For pipelines that mix barcode types (e.g., a packing slip with both an EAN-13 product code and a Code128 tracking number), combine the expected formats:
:path=/static-assets/barcode/content-code-examples/how-to/checksum-and-format-validation/multi-format-combine.cs
using IronBarCode;
// Combine multiple format flags with | to scan for more than one symbology
// in a single pass. Each BarcodeResult.BarcodeType identifies which format
// was decoded, enabling downstream routing logic per symbology.
var options = new BarcodeReaderOptions
{
ExpectBarcodeTypes = BarcodeEncoding.EAN13 | BarcodeEncoding.Code128,
ExpectMultipleBarcodes = true
};
Imports IronBarCode
' Combine multiple format flags with Or to scan for more than one symbology
' in a single pass. Each BarcodeResult.BarcodeType identifies which format
' was decoded, enabling downstream routing logic per symbology.
Dim options As New BarcodeReaderOptions With {
.ExpectBarcodeTypes = BarcodeEncoding.EAN13 Or BarcodeEncoding.Code128,
.ExpectMultipleBarcodes = True
}
Each returned BarcodeResult.BarcodeType identifies which format was decoded, enabling downstream routing.
Which Symbologies Support Checksum Validation?
Not all barcode formats use checksums in the same way. The following table maps common symbologies to their error-detection characteristics, which informs how aggressively to set ConfidenceThreshold and RemoveFalsePositive for each format:
| Symbology | Checksum Type | Mandatory? | Recommendation |
|---|---|---|---|
| EAN-13 / EAN-8 | Mod10 | Yes | Default settings sufficient; checksum always enforced |
| UPC-A / UPC-E | Mod10 | Yes | Default settings sufficient; check digit auto-corrected on write |
| Code128 | Weighted Mod103 | Yes | Default settings sufficient; mandatory per specification |
| Code39 | Mod43 | Optional | Raise ConfidenceThreshold to 0.8+ and enable RemoveFalsePositive |
| Codabar | Mod16 | Optional | Same as Code39; use confidence as the quality gate |
| ITF | Mod10 | Optional | Enable RemoveFalsePositive for interleaved formats |
| QRCode / DataMatrix | Reed-Solomon ECC | Always | Structural error correction; no additional configuration needed |
| PDF417 | Reed-Solomon ECC | Always | Same as QR/DataMatrix; error correction is inherent |
For 2D symbologies such as QR, DataMatrix, and PDF417, error correction is integrated into the encoding structure. These formats can recover from partial damage without depending on a simple check digit. The ConfidenceThreshold still applies during the ML detection phase, while the decode step benefits from the symbology's built-in redundancy.
Now that both techniques are understood, let's combine them into a single production-ready validation pattern.
How to Combine Checksums with Format Constraints?
The production-ready pattern sets ExpectBarcodeTypes, RemoveFalsePositive, ConfidenceThreshold, and Speed in a single BarcodeReaderOptions object. Together they form a layered gate: format constraints narrow the search space, checksum validation ensures data integrity, confidence thresholding filters marginal decodes, and false-positive removal adds a secondary verification pass.
Input
Three POS scan barcodes from the pos-scans/ directory used as the success path: two EAN-13 and one UPC-A. A Code128 warehouse rack label is used as the failure path — the EAN-13/UPC-A constraint rejects it and logs a REJECT line.
pos-scan-1.png (success)
pos-scan-2.png (success)
pos-scan-3.png (success)
warehouse-rack.png (failure — Code128 rejected)
:path=/static-assets/barcode/content-code-examples/how-to/checksum-and-format-validation/combined-validation.cs
using IronBarCode;
// Layered validation for retail POS: EAN-13, UPC-A, and UPC-E only.
// Each property adds a distinct filter to the read pipeline.
var options = new BarcodeReaderOptions
{
// Layer 1: format constraint, accept only retail symbologies
ExpectBarcodeTypes = BarcodeEncoding.EAN13 | BarcodeEncoding.UPCA | BarcodeEncoding.UPCE,
// Layer 2: confidence threshold, reject decodes below 80%
ConfidenceThreshold = 0.8,
// Layer 3: false-positive removal, runs a secondary verification pass
RemoveFalsePositive = true,
Speed = ReadingSpeed.Balanced,
ExpectMultipleBarcodes = false,
// Require 3 agreeing scan lines to reduce phantom reads from noisy images
MinScanLines = 3
};
string[] scanFiles = Directory.GetFiles("pos-scans/", "*.png");
foreach (string file in scanFiles)
{
BarcodeResults results = BarcodeReader.Read(file, options);
if (results.Count == 0)
{
// No barcode passed all validation layers
Console.Error.WriteLine($"REJECT {Path.GetFileName(file)}: "
+ "no valid EAN-13/UPC barcode (checksum, confidence, or format mismatch)");
continue;
}
BarcodeResult primary = results.First();
// Post-read assertion: verify the decoded format matches expectations.
// ExpectBarcodeTypes already constrains the reader; this check documents
// intent and surfaces unexpected results during future changes.
if (primary.BarcodeType != BarcodeEncoding.EAN13
&& primary.BarcodeType != BarcodeEncoding.UPCA
&& primary.BarcodeType != BarcodeEncoding.UPCE)
{
Console.Error.WriteLine($"UNEXPECTED FORMAT {Path.GetFileName(file)}: "
+ $"got {primary.BarcodeType}, expected EAN-13/UPC");
continue;
}
Console.WriteLine($"OK {Path.GetFileName(file)}: [{primary.BarcodeType}] {primary.Value}");
}
Imports IronBarCode
Imports System.IO
' Layered validation for retail POS: EAN-13, UPC-A, and UPC-E only.
' Each property adds a distinct filter to the read pipeline.
Dim options As New BarcodeReaderOptions With {
' Layer 1: format constraint, accept only retail symbologies
.ExpectBarcodeTypes = BarcodeEncoding.EAN13 Or BarcodeEncoding.UPCA Or BarcodeEncoding.UPCE,
' Layer 2: confidence threshold, reject decodes below 80%
.ConfidenceThreshold = 0.8,
' Layer 3: false-positive removal, runs a secondary verification pass
.RemoveFalsePositive = True,
.Speed = ReadingSpeed.Balanced,
.ExpectMultipleBarcodes = False,
' Require 3 agreeing scan lines to reduce phantom reads from noisy images
.MinScanLines = 3
}
Dim scanFiles As String() = Directory.GetFiles("pos-scans/", "*.png")
For Each file As String In scanFiles
Dim results As BarcodeResults = BarcodeReader.Read(file, options)
If results.Count = 0 Then
' No barcode passed all validation layers
Console.Error.WriteLine($"REJECT {Path.GetFileName(file)}: " &
"no valid EAN-13/UPC barcode (checksum, confidence, or format mismatch)")
Continue For
End If
Dim primary As BarcodeResult = results.First()
' Post-read assertion: verify the decoded format matches expectations.
' ExpectBarcodeTypes already constrains the reader; this check documents
' intent and surfaces unexpected results during future changes.
If primary.BarcodeType <> BarcodeEncoding.EAN13 AndAlso
primary.BarcodeType <> BarcodeEncoding.UPCA AndAlso
primary.BarcodeType <> BarcodeEncoding.UPCE Then
Console.Error.WriteLine($"UNEXPECTED FORMAT {Path.GetFileName(file)}: " &
$"got {primary.BarcodeType}, expected EAN-13/UPC")
Continue For
End If
Console.WriteLine($"OK {Path.GetFileName(file)}: [{primary.BarcodeType}] {primary.Value}")
Next
Output
Success Path
All three POS scan images made it through. The reader returned values of 5901234123471, 4006381333931, and 012345678905. Each one matched the EAN13 | UPCA | UPCE filter, had a valid Mod10 checksum, and confidence was above 0.8.
Failure Path
Setting MinScanLines to 3 increases the minimum number of agreeing scan lines required for a 1D barcode to be valid; the default is 2. Raising this value lowers the risk of phantom reads from noisy scan lines but may cause thin or partially damaged barcodes to be missed. In retail POS environments with clean printed labels, a value of 3 is a conservative choice that strengthens validation without affecting throughput.
The post-read BarcodeType assertion is defense-in-depth: ExpectBarcodeTypes already filters, but the explicit check documents intent and catches configuration drift without runtime cost. For speed tuning, ReadingSpeed.Faster suits clean machine-printed labels; Detailed and ExtremeDetail recover damaged or poorly lit barcodes at the cost of longer scan times.
What Are My Next Steps?
This article covered IronBarcode's implicit checksum validation model, the BarcodeEncoding flags enum for format-constrained reads, and a combined validation pattern using ExpectBarcodeTypes, ConfidenceThreshold, RemoveFalsePositive, and MinScanLines as layered quality gates.
For further reading, explore these resources:
- IronBarcode Tutorials — Reading Barcodes for end-to-end reading walkthroughs.
- False Positive Prevention for the
RemoveFalsePositivemechanism in detail. - Confidence Threshold Examples for ML-based detection tuning.
- Output Data Formats for
BarcodeResultproperty reference. - Image Correction How-To for filters that improve decode accuracy.
- BarcodeReaderOptions API Reference for complete configuration documentation.
- BarcodeEncoding API Reference for the full list of supported symbologies.
Get a free trial license to test every feature in a live environment, or view licensing options when the pipeline is ready for production.
Frequently Asked Questions
What is barcode checksum validation?
Barcode checksum validation is a process that ensures the accuracy of barcode data by verifying the calculated checksum against the value encoded within the barcode. This helps in detecting errors in the scanning process.
How does IronBarcode handle checksum validation?
IronBarcode implicitly handles checksum validation by calculating the checksum for the barcode data and verifying it against the encoded checksum, ensuring data integrity during the scanning process.
What are BarcodeEncoding filters?
BarcodeEncoding filters in IronBarcode allow you to specify which barcode formats to read or ignore during scanning, enabling more accurate and efficient barcode processing by focusing on specific barcode types.
Can IronBarcode perform combined validation?
Yes, IronBarcode can perform combined validation by checking both the checksum and the format of barcodes during the scanning process, ensuring that only valid and correctly formatted barcodes are processed.
Is it possible to constrain barcode reads by format in C# with IronBarcode?
Yes, IronBarcode allows you to constrain barcode reads by specifying the formats you want to include or exclude, ensuring that your application processes only relevant barcode types.
Why is format-aware reading important in barcode processing?
Format-aware reading is important because it allows your application to process only specific types of barcodes, improving speed and accuracy by ignoring irrelevant or unsupported barcode formats.
How do I implement format-aware reading in IronBarcode?
To implement format-aware reading in IronBarcode, use BarcodeEncoding filters to specify the barcode formats you wish to read. This can be done through the library's API, which allows for precise control over barcode scanning requirements.
What are the benefits of using IronBarcode for barcode validation?
IronBarcode offers several benefits for barcode validation, including robust checksum verification, format-aware reading, and the ability to handle a wide range of barcode standards, ensuring high accuracy and flexibility in barcode processing.

