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);
$vbLabelText   $csharpLabel

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:

Checksum Validation Model Comparison: IronBarcode vs. Aspose.BarCode
AspectIronBarcodeAspose.BarCode
Validation triggerAutomatic; runs during every decodeExplicit: ChecksumValidation.On / Off / Default
Developer action requiredNone; invalid barcodes are excluded from resultsSet BarcodeSettings.ChecksumValidation before reading
Checksum disableNot exposed; checksums are always enforced for mandatory formatsYes; ChecksumValidation.Off skips verification
Optional-checksum formats (Code39)Uses Confidence + RemoveFalsePositive to filter low-quality readsExplicitly enable with EnableChecksum.Yes
Failure behaviorBarcode silently omitted from resultsBarcode 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).

Code128 barcode encoding RACK-A1-LOT-7382 used as the warehouse rack scan input

warehouse-rack.png (success path)

Blank white image with no barcode to trigger the empty result 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");
}
$vbLabelText   $csharpLabel

Output

Success Path

Console output showing Code128 RACK-A1-LOT-7382 decoded above the confidence threshold

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

Console output showing WARN no valid barcodes found for the blank image input

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.

Common BarcodeEncoding Values
ValueCategoryDescriptionChecksum
BarcodeEncoding.AllMetaAll supported formats (default behavior)Per-format
BarcodeEncoding.AllOneDimensionalMetaAll linear (1D) formats including stackedPer-format
BarcodeEncoding.AllTwoDimensionalMetaAll matrix/grid (2D) formatsPer-format
BarcodeEncoding.Code1281DHigh-density alphanumeric (logistics, shipping)Mandatory (weighted Mod103)
BarcodeEncoding.EAN131DRetail product identification, 13 digitsMandatory (Mod10)
BarcodeEncoding.QRCode2DHigh-capacity matrix (URLs, structured data)Reed-Solomon ECC
BarcodeEncoding.Code391DAlphanumeric (defense, automotive)Optional (Mod43)
BarcodeEncoding.UPCA1DNorth American retail, 12 digitsMandatory (Mod10)
BarcodeEncoding.DataMatrix2DCompact matrix (electronics, pharma)Reed-Solomon ECC
BarcodeEncoding.PDF4172DStacked (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).

Code128 barcode encoding SHIP-2024-00438 used as the shipping label input

shipping-label.png (success path — Code128 matches constraint)

QR code used as the format-mismatch failure path for the Code128-only constrained read

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}");
}
$vbLabelText   $csharpLabel

Output

Success Path

Console output showing constrained read finding 1 Code128 barcode and broad read confirming it

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

Console output showing constrained read returning 0 results for a QR code image

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
};
$vbLabelText   $csharpLabel

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:

Checksum Characteristics by Symbology
SymbologyChecksum TypeMandatory?Recommendation
EAN-13 / EAN-8Mod10YesDefault settings sufficient; checksum always enforced
UPC-A / UPC-EMod10YesDefault settings sufficient; check digit auto-corrected on write
Code128Weighted Mod103YesDefault settings sufficient; mandatory per specification
Code39Mod43OptionalRaise ConfidenceThreshold to 0.8+ and enable RemoveFalsePositive
CodabarMod16OptionalSame as Code39; use confidence as the quality gate
ITFMod10OptionalEnable RemoveFalsePositive for interleaved formats
QRCode / DataMatrixReed-Solomon ECCAlwaysStructural error correction; no additional configuration needed
PDF417Reed-Solomon ECCAlwaysSame 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.

EAN-13 barcode encoding 5901234123471 used as POS scan input 1

pos-scan-1.png (success)

EAN-13 barcode encoding 4006381333931 used as POS scan input 2

pos-scan-2.png (success)

UPC-A barcode encoding 012345678905 used as POS scan input 3

pos-scan-3.png (success)

Code128 barcode encoding RACK-A1-LOT-7382 used as the combined-validation failure path input

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}");
}
$vbLabelText   $csharpLabel

Output

Success Path

Console output showing all 3 POS scan barcodes accepted with OK status and decoded values

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

Console output showing warehouse-rack Code128 barcode rejected by the EAN-13/UPC-A filter

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:

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.

Darrius Serrant
Full Stack Software Engineer (WebOps)

Darrius Serrant holds a Bachelor’s degree in Computer Science from the University of Miami and works as a Full Stack WebOps Marketing Engineer at Iron Software. Drawn to coding from a young age, he saw computing as both mysterious and accessible, making it the perfect medium for creativity ...

Read More
Ready to Get Started?
Nuget Downloads 2,124,798 | Version: 2026.3 just released
Still Scrolling Icon

Still Scrolling?

Want proof fast? PM > Install-Package BarCode
run a sample watch your string become a barcode.