Skip to main content

Error Diagnosis Guide

When a PCD program fails certification, the compiler aborts with a diagnostic message. This guide explains every error type and how to fix it.

How Certification Works

The CMF (Coherence Metric Framework) computes 7 metrics for your program. If any metric is out of bounds, or if the master metric Φ_c ≠ 1.000, compilation is aborted — not warned.
brikc check broken.pcd
# ✗ Φ_c = 0.875 (required: 1.000)
# ✗ δ = 0.125 (1 unused input)
# Compilation aborted.

Error: Unused Inputs (δ > 0)

What it means: Your function declares parameters that are never used.
// BAD: parameter `b` is never used
fn add_one(a, b) {
    return a + 1;
}
Diagnostic:
δ = 0.500 (1 of 2 inputs unused)
Φ_c = 0.500 != 1.000
Fix: Remove the unused parameter, or use it:
// GOOD: both parameters used
fn add(a, b) {
    return a + b;
}

// Or remove unused parameter
fn add_one(a) {
    return a + 1;
}
If you intentionally need to ignore a parameter (e.g., for interface compatibility), prefix it with _:
fn handler(event, _context) {
    return process(event);
}
The _ prefix tells the CMF to exclude it from the unused input count.

Error: Unreachable Code Paths

What it means: Some code paths never execute, indicating dead branches.
// BAD: the else branch after return is unreachable
fn check(x) {
    if (x > 0) {
        return "positive";
    }
    return "non-positive";
    let dead = "this never executes";  // unreachable!
}
Fix: Remove dead code after return or OUTPUT:
fn check(x) {
    if (x > 0) {
        return "positive";
    }
    return "non-positive";
}

Error: Missing OUTPUT on Some Paths

What it means: Not all control flow paths lead to an OUTPUT or return.
// BAD: what if x == 0?
PC classify {
    let x = MC_63.ENV("ARGV_1");
    if (x > 0) {
        OUTPUT "positive";
    }
    if (x < 0) {
        OUTPUT "negative";
    }
    // Falls through with no OUTPUT when x == 0!
}
Diagnostic:
Φ_c < 1.000: not all paths produce output
Fix: Add a final OUTPUT that catches all remaining cases:
PC classify {
    let x = MC_63.ENV("ARGV_1");
    if (x > 0) {
        OUTPUT "positive";
    }
    if (x < 0) {
        OUTPUT "negative";
    }
    OUTPUT "zero";    // catches x == 0
}

Error: Type Mismatch at Monomer Boundary

What it means: You passed a value of the wrong type to a monomer.
// BAD: MC_00.ADD8 expects numbers, not strings
let result = MC_00.ADD8("hello", 5);
Diagnostic:
Type error: MC_00.ADD8 expects (u8, u8), got (string, i64)
Fix: Ensure argument types match the monomer’s signature. See the Monomer Reference for exact signatures.

Error: DIV8 Not Destructured

What it means: MC_03.DIV8 returns a tuple (quotient, remainder), and you’re treating it as a single value.
// BAD: DIV8 returns a tuple
let result = MC_03.DIV8(10, 3);
let doubled = result * 2;  // Error: can't multiply a tuple
Diagnostic:
Type error: expected i64, got Tuple([i64, i64])
Fix: Always destructure DIV8:
let (q, r) = MC_03.DIV8(10, 3);
let doubled = q * 2;    // OK: q is i64

Error: Division by Zero

What it means: MC_03.DIV8 or MC_04.MOD8 received 0 as the divisor.
let (q, r) = MC_03.DIV8(10, 0);  // Runtime error
Fix: Guard against zero before dividing:
fn safe_div(a, b) {
    if (b == 0) {
        return (0, 0);
    }
    return MC_03.DIV8(a, b);
}
Or use try/catch:
try {
    let (q, r) = MC_03.DIV8(a, b);
    OUTPUT q;
} catch (err) {
    OUTPUT 0;
}

Error: MAX_DEPTH Exceeded

What it means: Your program has more than 256 levels of nesting.
// BAD: deeply nested ifs exceed MAX_DEPTH=256
if (a) { if (b) { if (c) { ... // 257+ levels
Diagnostic:
Parser error: MAX_DEPTH (256) exceeded at line N
Fix: Flatten nested logic using functions:
fn check_level_1(x) {
    // handle first batch of checks
    return intermediate_result;
}

fn check_level_2(x) {
    let r1 = check_level_1(x);
    // handle second batch
    return final_result;
}

Error: Circular Import

What it means: Module A imports B, and B imports A (directly or transitively).
a.pcd imports b.pcd
b.pcd imports a.pcd  → circular!
Diagnostic:
Import error: circular dependency detected: a.pcd → b.pcd → a.pcd
Fix: Extract shared code into a third module:
common.pcd    ← shared functions
a.pcd imports common.pcd
b.pcd imports common.pcd

Error: While Loop SSA Bug

What it means: Variables inside a while loop body don’t update correctly across iterations.
// BUGGY: pos may not update due to WhileLoop SSA bug
let pos = 0;
while (pos < max) {
    let pos = pos + 1;  // May read stale value!
}
Fix: Replace while with loop(N) + if:
let pos = 0;
loop(max) as _i {
    if (pos < max) {
        let pos = pos + 1;  // Works correctly with loop(N)
    }
}
This is a known limitation of the current planner. The while SSA bug captures variable versions before the loop body instead of after. Always use loop(N) in production code.

Error: Out of Bounds Array Access

let arr = [1, 2, 3];
let x = arr[5];  // Runtime error: index 5 out of bounds (len=3)
Fix: Check length before accessing:
let arr = [1, 2, 3];
let idx = 5;
if (idx < len(arr)) {
    let x = arr[idx];
} else {
    // handle out of bounds
}
Or use try/catch:
try {
    let x = arr[idx];
} catch (err) {
    let x = 0;  // default value
}

Error: String Index Out of Bounds

let s = "hello";
let ch = MC_45.CHAR_AT(s, 10);  // Runtime error
Fix: Always check MC_43.LEN first:
let s = "hello";
let idx = 10;
let slen = MC_43.LEN(s);
if (idx < slen) {
    let ch = MC_45.CHAR_AT(s, idx);
} else {
    let ch = "";
}

Error: Unresolved Import

import "stdlib/nonexistent.pcd";  // File not found
Diagnostic:
Import error: file not found: stdlib/nonexistent.pcd
Fix: Check that the file exists and the path is correct. Available stdlib modules:
ModulePath
mathstdlib/math.pcd
stringstdlib/string.pcd
arraystdlib/array.pcd
iostdlib/io.pcd
fmtstdlib/fmt.pcd
jsonstdlib/json.pcd

Reading CMF Output

When brikc check outputs the full CMF profile, here’s how to read it:
{
  "cmf": {
    "e":    4,        // Operational complexity: 4 edges in graph (OK)
    "h":    0.44,     // Signature distance: moderate type transformation (OK)
    "s":    0.000,    // Structural entropy: no branches (excellent)
    "c":    1,        // Cyclomatic complexity: 1 path (excellent)
    "t":    0,        // Termination depth: no loops (excellent)
    "δ":    0.000,    // Unused inputs: none (required for Ω=1)
    "Φ_c":  1.000     // Closure: certified!
  },
  "omega": 1
}
Red flags to look for:
  • δ > 0 — you have unused parameters
  • Φ_c < 1.000 — the circuit is not closed; check δ and ensure all paths produce output
  • Very high s — excessive branching; consider simplifying control flow
  • Very high t — deep loop nesting; consider flattening with functions

Quick Troubleshooting Table

SymptomLikely CauseFix
Φ_c < 1.000Unused inputs or missing OUTPUT pathsRemove unused params, add fallback OUTPUT
δ > 0Unused function parametersRemove or prefix with _
Type error at monomer callWrong argument typesCheck Monomer Reference
Tuple type mismatchForgot to destructure DIV8Use let (q, r) = MC_03.DIV8(...)
Parser MAX_DEPTHToo many nested blocksExtract into functions
While loop vars don’t updateWhileLoop SSA bugUse loop(N) instead
Circular importA imports B, B imports AExtract shared code to third module
Runtime crashDivision by zero or OOB accessGuard with if or try/catch