Verbatim compiler output
GCC 13.2, Clang 17, MSVC 19.39, javac, CPython, V8. The exact diagnostic the toolchain prints, not a paraphrase.
Reference
40 errors across 5 languages, every one paired with the verbatim compiler output, root cause, and the Broken vs Fixed snippet that resolves it.
Overview
main runs. Undefined behavior compiles, runs, then misbehaves silently. Each entry shows the actual compiler output (GCC, Clang, javac, CPython, V8), explains why the compiler emitted it, and pairs Broken vs Fixed code so you can diff the change directly.At a glance
GCC 13.2, Clang 17, MSVC 19.39, javac, CPython, V8. The exact diagnostic the toolchain prints, not a paraphrase.
Each error ships with the snippet that triggers it and the patch that resolves it. Diff the change side by side.
Not just the fix. The reason the compiler emits the message, so the next instance of the pattern looks familiar.
Section 1 of 47
Each error follows the same 3-block structure. Broken reproduces the failure. Compiler output is the verbatim message the toolchain emits (different vendors print different formats for the same bug, so we show GCC and Clang side by side where they diverge). Fixed shows the patch.
Versions used for the verbatim output: GCC 13.2, Clang 17, MSVC 19.39, OpenJDK 21, CPython 3.12, Node 20 (V8 11.3). Older toolchains may format the same diagnostic slightly differently; the cause and fix do not change.
Section 2 of 47
Cause: missing semicolon. The C grammar requires one after every statement. The compiler reports the column of the token after the missing one, which is why the message points at the closing brace.
main.c:5:1: error: expected ';' before '}' token
/* Broken */
#include <stdio.h>
int main(void) {
int x = 5
printf("%d\n", x);
return 0;
} /* Fixed: semicolon after the declaration */
#include <stdio.h>
int main(void) {
int x = 5;
printf("%d\n", x);
return 0;
} Section 3 of 47
Cause: calling a function before declaring it. The C89 standard let the compiler infer int return type; C99 and later make this an error (downgraded to a warning by default in GCC, promoted to error with -Werror or in any course that enables strict flags).
main.c:4:5: warning: implicit declaration of function 'strlen' [-Wimplicit-function-declaration]
/* Broken: missing #include <string.h> */
#include <stdio.h>
int main(void) {
int n = strlen("hi");
printf("%d\n", n);
return 0;
} /* Fixed: include the header that declares strlen */
#include <stdio.h>
#include <string.h>
int main(void) {
int n = (int)strlen("hi");
printf("%d\n", n);
return 0;
} Section 4 of 47
Cause: assigning a pointer of one type to a pointer of an incompatible type without a cast. Common after refactoring a function signature.
main.c:6:11: warning: assignment to 'int *' from incompatible pointer type 'char *' [-Wincompatible-pointer-types]
/* Broken */
#include <stdlib.h>
int main(void) {
int *p;
char *q = malloc(10);
p = q; /* incompatible pointer types */
return 0;
} /* Fixed: cast through void* or align the types */
#include <stdlib.h>
int main(void) {
int *p = malloc(10 * sizeof(int));
char *q = (char *)p; /* explicit byte view */
free(p);
return 0;
} Section 5 of 47
Cause: the compile step succeeded but the linker cannot find the symbol. Five subcauses: missing source file in the link line, missing -l flag for a library, mismatched function signature between declaration and definition, an extern function never defined, or a static function visible only inside the file that defines it.
/usr/bin/ld: /tmp/cc6xK4Yt.o: in function `main': main.c:5: undefined reference to `compute' collect2: error: ld returned 1 exit status
/* Broken: helper.c not compiled or linked */
/* main.c */
extern int compute(int);
int main(void) {
return compute(42);
}
/* compile: gcc main.c -o app -> undefined reference to compute */ /* Fixed: include the implementation in the link line */
/* main.c (unchanged) */
extern int compute(int);
int main(void) {
return compute(42);
}
/* helper.c */
int compute(int x) { return x + 1; }
/* compile: gcc main.c helper.c -o app */ Section 6 of 47
Cause: dereferencing an invalid pointer (NULL, freed, uninitialized) or writing past an allocated buffer. Not a compiler error; the program compiles, runs, then dies. Reproduce under Valgrind: valgrind --leak-check=full ./app reports the line that triggered the invalid access. Or under GDB: run, hit segfault, bt for backtrace.
Five canonical sources of a segfault in coursework C code:
malloc returned NULL (out of memory or zero-size request), the code did not check.char buf[10] corrupts the return address. The crash happens on function return, not on the write itself.malloc'd memory corrupts allocator metadata. The crash happens on the next malloc or free, far from the offending write.Segmentation fault (core dumped)
/* Broken: dereferencing NULL */
#include <stdio.h>
int main(void) {
int *p = NULL;
*p = 42; /* segfault here */
printf("%d\n", *p);
return 0;
} /* Fixed: allocate before dereferencing */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
if (!p) return 1;
*p = 42;
printf("%d\n", *p);
free(p);
return 0;
} Section 7 of 47
Cause: writing past the end of a stack-allocated buffer triggered the canary check inserted by GCC -fstack-protector-strong. The runtime kills the process before the buffer overflow can return to attacker-controlled code.
*** stack smashing detected ***: terminated Aborted (core dumped)
/* Broken: writing 20 bytes into a 10-byte buffer */
#include <string.h>
int main(void) {
char buf[10];
strcpy(buf, "this string is too long");
return 0;
} /* Fixed: bound the copy with strncpy or snprintf */
#include <string.h>
#include <stdio.h>
int main(void) {
char buf[10];
snprintf(buf, sizeof(buf), "%s", "this string is too long");
return 0;
} Section 8 of 47
Cause: printf format string disagrees with the argument. GCC -Wformat catches most cases at compile time. Mismatch causes garbage output at best, stack corruption at worst.
main.c:5:18: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
/* Broken */
#include <stdio.h>
int main(void) {
double pi = 3.14;
printf("pi = %d\n", pi); /* %d expects int */
return 0;
} /* Fixed: %f for double, or cast intentionally */
#include <stdio.h>
int main(void) {
double pi = 3.14;
printf("pi = %f\n", pi);
return 0;
} Section 9 of 47
Cause: a function declared to return a value has a code path that does not. The standard says the return value is undefined; some compilers warn, others error with strict flags.
main.c:7:1: warning: control reaches end of non-void function [-Wreturn-type]
/* Broken */
int sign(int x) {
if (x > 0) return 1;
if (x < 0) return -1;
/* zero case falls off the end */
} /* Fixed: handle every path */
int sign(int x) {
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
} Section 10 of 47
Cause: comparing or assigning NULL to an integer, or vice versa. Frequent in loops that confuse strchr (returns char *) with strcmp (returns int).
main.c:6:11: warning: assignment to 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]
/* Broken */
#include <string.h>
int main(void) {
char s[] = "hello";
int found = strchr(s, 'e'); /* strchr returns char* */
return found;
} /* Fixed: store the pointer or test against NULL */
#include <string.h>
int main(void) {
char s[] = "hello";
char *found = strchr(s, 'e');
return found != NULL;
} Section 11 of 47
Cause: accessing a member of a struct whose definition the compiler has not seen. The header forward-declared the type but never defined it, or the include order is wrong.
main.c:5:13: error: dereferencing pointer to incomplete type 'struct Node'
/* Broken: forward decl without definition */
struct Node;
int main(void) {
struct Node *n = 0;
return n->value; /* incomplete type */
} /* Fixed: define the struct before use */
struct Node { int value; struct Node *next; };
int main(void) {
struct Node n = {42, 0};
return n.value;
} Section 12 of 47
Cause: C++ does not allow the implicit pointer-to-int conversion that C tolerates. A pointer never becomes a number without an explicit reinterpret_cast or intptr_t conversion.
main.cpp:4:9: error: invalid conversion from 'int*' to 'int' [-fpermissive]
// Broken
int main() {
int x = 5;
int y = &x; // pointer to int implicit conversion
return y;
} // Fixed: dereference the pointer, or capture address explicitly
#include <cstdint>
int main() {
int x = 5;
int y = x; // copy the value
std::intptr_t addr = reinterpret_cast<std::intptr_t>(&x);
return y + (addr & 1);
} Section 13 of 47
Cause: the overload resolver could not find an exact match. Either no overload exists, or implicit conversions cannot reach any candidate without ambiguity.
main.cpp:8:5: error: no matching function for call to 'add' note: candidate function template not viable: requires 2 arguments, but 3 were provided
// Broken: calling add with wrong arity
template <typename T>
T add(T a, T b) { return a + b; }
int main() {
return add(1, 2, 3); // no 3-arg overload
} // Fixed: variadic template or correct arity
#include <numeric>
template <typename... T>
auto add(T... xs) { return (xs + ...); } // fold expression (C++17)
int main() {
return add(1, 2, 3); // 6
} Section 14 of 47
Cause: passing a temporary (rvalue) to a function that takes a non-const reference. Non-const references can only bind to lvalues so the function cannot mutate a temporary that disappears at the next semicolon.
main.cpp:6:8: error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
// Broken
void inc(int& x) { x += 1; }
int main() {
inc(5); // 5 is a temporary
return 0;
} // Fixed: pass a named variable, or accept const& or by value
void inc(int& x) { x += 1; }
int main() {
int n = 5;
inc(n);
return n; // 6
} Section 15 of 47
Cause: a class with a deleted copy constructor (commonly std::unique_ptr, std::thread, anything RAII-wrapping an exclusive resource) is being copied. Move it instead.
main.cpp:6:24: error: use of deleted function 'std::unique_ptr...&)'
// Broken: copying unique_ptr
#include <memory>
int main() {
auto a = std::make_unique<int>(42);
auto b = a; // deleted copy constructor
return *b;
} // Fixed: move-transfer ownership
#include <memory>
#include <utility>
int main() {
auto a = std::make_unique<int>(42);
auto b = std::move(a); // a is now empty
return *b;
} Section 16 of 47
Cause: a class with at least one virtual function had one defined inline and at least one declared but not defined. The compiler emits the vtable in the translation unit that defines the first non-inline virtual function; if no TU defines it, the linker fails.
/usr/bin/ld: /tmp/main.o:(.data.rel.ro._ZTV3Foo[_ZTV3Foo]+0x10): undefined reference to `Foo::greet()' collect2: error: ld returned 1 exit status
// Broken: greet declared, never defined
struct Foo {
virtual void greet(); // declared
virtual ~Foo() {}
};
int main() {
Foo f;
f.greet();
return 0;
} // Fixed: define every declared virtual (or pure-virtual = 0)
struct Foo {
virtual void greet() {} // defined inline
virtual ~Foo() {}
};
int main() {
Foo f;
f.greet();
return 0;
} Section 17 of 47
Cause: passing a const object to a function that takes a non-const reference. Symmetric to the rvalue binding error but driven by qualifiers, not value category.
main.cpp:7:9: error: passing 'const std::string' as 'this' argument discards qualifiers [-fpermissive]
// Broken: calling non-const member on a const object
#include <string>
int main() {
const std::string s = "hi";
s.clear(); // clear() is not const
return 0;
} // Fixed: drop const, or copy
#include <string>
int main() {
std::string s = "hi";
s.clear();
return 0;
} Section 18 of 47
Cause: two candidates require different implicit conversions and neither dominates. Common when mixing int, long, double, or when overloading with both pointer and integer parameters.
main.cpp:8:5: error: call to 'add' is ambiguous note: candidate function
// Broken: ambiguous between int and double overloads on 0
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int main() {
return add(1, 2.0); // 1 is int, 2.0 is double, neither dominates
} // Fixed: force one type
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int main() {
return add(1.0, 2.0); // both double
} Section 19 of 47
Cause: an exception escaped main or a destructor without being caught. C++ calls std::terminate, which by default calls abort.
Three places where exceptions become terminations: escaping main with no catch on the path, throwing from a destructor during stack unwinding (a second exception during cleanup of the first), and throwing across a thread boundary without joining via std::future. Coursework that uses STL containers often hits this via vector::at, map::at, stoi, or regex_match, all of which throw on bad input.
terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check: __n (which is 5) >= this->size() (which is 3) Aborted (core dumped)
// Broken: vector::at throws on out-of-range
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3};
return v.at(5); // throws out_of_range
} // Fixed: bounds-check or catch
#include <vector>
#include <stdexcept>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3};
try {
return v.at(5);
} catch (const std::out_of_range& e) {
std::cerr << e.what() << '\n';
return 1;
}
} Section 20 of 47
Cause: forward-declared a stream type with class std::ostream; but never included <iostream>. Forward declarations of standard library types are not portable; the spec lets implementations use undisclosed inheritance trees.
main.cpp:5:25: error: invalid use of incomplete type 'class std::basic_ostream<char>'
// Broken
namespace std { class ostream; }
void log(std::ostream& os) {
os << "hi"; // incomplete type
} // Fixed: include the real header
#include <iostream>
void log(std::ostream& os) {
os << "hi";
} Section 21 of 47
Cause: two threads access the same memory without synchronization, at least one of which writes. Undefined behavior per the C++ memory model. Compile with -fsanitize=thread to catch it.
Why "undefined" is harsher than "wrong": the C++ memory model says a program with a data race may produce any output. The compiler is allowed to assume races never happen and aggressively optimize based on that assumption. The same code can produce different results on the same input on different runs, on different hardware (x86 vs ARM), and at different optimization levels. The fix is always synchronization, either via std::atomic for simple counters or std::mutex for non-atomic operations on shared data.
WARNING: ThreadSanitizer: data race (pid=1234) Write of size 4 at 0x000000abcdef by thread T1: ... Previous read of size 4 at 0x000000abcdef by thread T2: ...
// Broken: unsynchronized shared counter
#include <thread>
int counter = 0;
void inc() { for (int i = 0; i < 100000; i++) counter++; }
int main() {
std::thread a(inc), b(inc);
a.join(); b.join();
return counter; // less than 200000, races visible under TSan
} // Fixed: atomic counter or mutex
#include <thread>
#include <atomic>
std::atomic<int> counter{0};
void inc() { for (int i = 0; i < 100000; i++) counter.fetch_add(1); }
int main() {
std::thread a(inc), b(inc);
a.join(); b.join();
return counter; // exactly 200000
} Section 22 of 47
Cause: identifier the compiler cannot resolve. Misspelled name, missing import, or a method called on the wrong receiver.
Main.java:5: error: cannot find symbol Sytem.out.println("hi"); ^ symbol: variable Sytem location: class Main
// Broken: typo in System
public class Main {
public static void main(String[] args) {
Sytem.out.println("hi");
}
} // Fixed: spell it correctly
public class Main {
public static void main(String[] args) {
System.out.println("hi");
}
} Section 23 of 47
Cause: assigning a value of type A to a variable of type B without a legal conversion. The compiler shows both types so the fix is usually a cast or a conversion call.
Main.java:4: error: incompatible types: String cannot be converted to int int n = "42"; ^
// Broken
public class Main {
public static void main(String[] args) {
int n = "42"; // String to int, no implicit conv
}
} // Fixed: parse explicitly
public class Main {
public static void main(String[] args) {
int n = Integer.parseInt("42");
System.out.println(n);
}
} Section 24 of 47
Cause: a non-void method has a control path that falls off the end without returning. The compiler proves this with definite-assignment analysis.
Main.java:5: error: missing return statement }
// Broken
public class Main {
static int sign(int x) {
if (x > 0) return 1;
if (x < 0) return -1;
// zero case missing
}
} // Fixed
public class Main {
static int sign(int x) {
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
}
} Section 25 of 47
Cause: dereferencing a null reference. Java 14 added helpful NPEs (-XX:+ShowCodeDetailsInExceptionMessages, on by default since 15) that name the variable that was null.
Three structural sources of NPEs in coursework Java code: uninitialized field (declared with default-null initializer, never assigned before use); missing null check after lookup (map.get(key) returns null for absent keys); lazy initialization race (one thread reads the field while another initializes it). Modern Java strongly recommends Optional<T> for return types where null is a legitimate value, eliminating the null-check failure mode by making the absence explicit in the type system.
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "s" is null at Main.main(Main.java:4)
// Broken
public class Main {
public static void main(String[] args) {
String s = null;
System.out.println(s.length()); // NPE
}
} // Fixed: null check, or Optional, or non-null default
import java.util.Objects;
public class Main {
public static void main(String[] args) {
String s = null;
int len = Objects.requireNonNullElse(s, "").length();
System.out.println(len); // 0
}
} Section 26 of 47
Cause: reading or writing past the end of an array. The JVM bounds-checks every array access; the cost is amortized to negligible by JIT bounds-check elimination on provably safe loops.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3 at Main.main(Main.java:5)
// Broken
public class Main {
public static void main(String[] args) {
int[] a = {1, 2, 3};
for (int i = 0; i <= a.length; i++) { // <=, not <
System.out.println(a[i]);
}
}
} // Fixed: < length, or enhanced for
public class Main {
public static void main(String[] args) {
int[] a = {1, 2, 3};
for (int x : a) System.out.println(x);
}
} Section 27 of 47
Cause: structurally modifying a collection while iterating it via an enhanced for-loop. The iterator detects the modification count drift and throws.
The name is misleading. The exception fires on single-threaded modification too; it has nothing to do with multiple threads. The "concurrent" in the name refers to concurrent reads (the iterator) and writes (the modification) within one method. The 4 safe modification patterns are: use Iterator.remove() for in-place removal, use Collection.removeIf(predicate) for bulk removal, collect indices to remove and apply after the loop, or copy the collection before iterating and modify the original. Concurrent collections like CopyOnWriteArrayList and ConcurrentHashMap iterate over snapshots and never throw CME, at the cost of weaker consistency guarantees.
Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(...)
// Broken
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
for (int x : list) {
if (x == 2) list.remove(Integer.valueOf(2)); // CME
}
}
} // Fixed: use Iterator.remove or removeIf
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
list.removeIf(x -> x == 2);
System.out.println(list); // [1, 3]
}
} Section 28 of 47
Cause: casting a reference to a type the runtime object is not. Common in code that downcasts Object from a generic container without pattern matching.
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
// Broken
public class Main {
public static void main(String[] args) {
Object o = 42;
String s = (String) o; // ClassCastException
}
} // Fixed: pattern matching for instanceof (Java 16+)
public class Main {
public static void main(String[] args) {
Object o = 42;
if (o instanceof String s) {
System.out.println(s.length());
} else {
System.out.println("not a string");
}
}
} Section 29 of 47
Cause: invoking a method that declares a checked exception without either catching it or declaring it on the enclosing method. Java tracks checked exceptions in method signatures.
Main.java:5: error: unreported exception IOException; must be caught or declared to be thrown
// Broken
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) {
Files.readString(Path.of("config.txt")); // throws IOException
}
} // Fixed: declare or catch
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) throws IOException {
String s = Files.readString(Path.of("config.txt"));
System.out.println(s);
}
} Section 30 of 47
Cause: a local variable is read on a path the compiler cannot prove assigns it. Definite-assignment analysis is strict; even a logically-unreachable branch counts.
Main.java:8: error: variable result might not have been initialized return result; ^
// Broken
public class Main {
static int classify(int x) {
int result;
if (x > 0) result = 1;
if (x < 0) result = -1;
// zero path leaves result unassigned
return result;
}
} // Fixed: initialize at declaration or use else
public class Main {
static int classify(int x) {
int result;
if (x > 0) result = 1;
else if (x < 0) result = -1;
else result = 0;
return result;
}
} Section 31 of 47
Cause: running classes compiled with a newer JDK on an older runtime. Java 21 emits class file version 65, Java 17 emits 61, Java 11 emits 55, Java 8 emits 52.
Error: LinkageError occurred while loading main class Main java.lang.UnsupportedClassVersionError: Main has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 61.0
// Broken: compile with Java 21, run with Java 17
// $ javac --release 21 Main.java
// $ java Main # using a Java 17 java binary -> UnsupportedClassVersionError // Fixed: target the runtime version, or upgrade the JRE
// $ javac --release 17 Main.java
// $ java Main # works on Java 17+ Section 32 of 47
Cause: Python parser rejects the construct. The caret points at the column it choked on. Common causes: missing colon, mismatched parens, walrus operator before Python 3.8, f-string nesting issues.
File "main.py", line 2 if x > 0 ^ SyntaxError: expected ':'
# Broken
def sign(x):
if x > 0
return 1
return -1 # Fixed: colon after the if condition
def sign(x):
if x > 0:
return 1
return -1 Section 33 of 47
Cause: a colon-introduced block has nothing under it, or the indentation is inconsistent. Python 3 forbids mixing tabs and spaces inside the same block; set the editor to expand tabs to spaces.
File "main.py", line 3 return 1 ^ IndentationError: expected an indented block after 'if' statement on line 2
# Broken: missing body
def sign(x):
if x > 0:
return 1 # Fixed
def sign(x):
if x > 0:
return 1
return -1 Section 34 of 47
Cause: an operator was called on types that do not support it. Python only does implicit numeric conversions (int promotes to float). Strings and numbers do not combine without an explicit cast.
Traceback (most recent call last): File "main.py", line 2, in <module> print("age: " + 42) TypeError: can only concatenate str (not "int") to str
# Broken
age = 42
print("age: " + age) # Fixed: f-string, or str()
age = 42
print(f"age: {age}")
print("age: " + str(age)) Section 35 of 47
Cause: import cannot find the named module. Three subcauses: not installed in this interpreter (pip install in a different venv), typo, or wrong working directory for a local relative import.
The venv-pip skew is the most common cause and the hardest to spot. A typical setup has a system Python at /usr/bin/python3, a virtualenv Python at ./venv/bin/python, and a Homebrew Python at /opt/homebrew/bin/python3. Each has its own site-packages. Running pip install numpy uses whichever pip is first on PATH, which may not be the pip belonging to the python you run. The fix is always to use the explicit module form: python -m pip install numpy. The -m pip guarantees the install lands in the site-packages of the running interpreter.
Traceback (most recent call last): File "main.py", line 1, in <module> import numpy as np ModuleNotFoundError: No module named 'numpy'
# Broken
# $ python main.py
# main.py:
import numpy as np
print(np.array([1, 2, 3])) # Fixed: install into the right interpreter
# $ python -m pip install numpy
# Then re-run. Use python -m pip to avoid 'pip' pointing
# at a different interpreter than 'python'.
import numpy as np
print(np.array([1, 2, 3])) # [1 2 3] Section 36 of 47
Cause: indexing past the end of a list (or before the start with a negative index smaller than -len(list)). Slicing is more forgiving: list[100:] on a 3-element list returns [], not an error.
Traceback (most recent call last): File "main.py", line 3, in <module> print(nums[5]) IndexError: list index out of range
# Broken
nums = [1, 2, 3]
print(nums[5]) # Fixed: bounds-check or use .get() pattern
nums = [1, 2, 3]
i = 5
if 0 <= i < len(nums):
print(nums[i])
else:
print(None) Section 37 of 47
Cause: accessing a property on undefined or null. Most common cause: a function returned without an explicit return value, then the caller chained a property access.
TypeError: Cannot read properties of undefined (reading 'length') at Object.<anonymous> (/tmp/main.js:3:25)
// Broken
function firstWord(s) { s.split(' ')[0]; } // missing return
const w = firstWord("hello world");
console.log(w.length); // TypeError // Fixed: return, or use optional chaining
function firstWord(s) { return s.split(' ')[0]; }
const w = firstWord("hello world");
console.log(w?.length); // 5 Section 38 of 47
Cause: referencing an identifier never declared (no var, let, or const) or hoisted past its temporal dead zone. let and const are not accessible before their declaration line, even though the binding exists from the top of the block.
ReferenceError: count is not defined at Object.<anonymous> (/tmp/main.js:2:13)
// Broken: temporal dead zone
console.log(count); // TDZ error
let count = 5; // Fixed: declare before use
let count = 5;
console.log(count); Section 39 of 47
Cause: the parser hit a token it cannot match to any grammar rule. Common cause: JSON.parse on a string that is not valid JSON (single quotes, trailing commas, comments).
SyntaxError: Unexpected token '\'', "{'name': 'Ada'}" is not valid JSON
// Broken: single quotes not valid JSON
const raw = "{'name': 'Ada'}";
const obj = JSON.parse(raw); // Fixed: double quotes only in JSON
const raw = '{"name": "Ada"}';
const obj = JSON.parse(raw);
console.log(obj.name); // Ada Section 40 of 47
Cause: a promise rejected with no .catch handler. Since Node 15, the default is to crash the process. Adding process.on('unhandledRejection', ...) changes the behavior but the underlying bug remains.
The async/await trap: writing async function fetch() { throw new Error(); } and calling it without await returns a rejected promise that nobody handles. The function looks synchronous to the eye but returns a Promise. Always either await the call inside a try/catch, or attach .catch() to the returned promise. ESLint rule no-floating-promises (in @typescript-eslint) catches this at lint time; enable it on every TypeScript project.
UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().
// Broken
async function fetchData() {
throw new Error("network down");
}
fetchData(); // no .catch, no await // Fixed: await inside try/catch, or chain .catch
async function fetchData() {
throw new Error("network down");
}
fetchData().catch((e) => console.error(e.message));
// Or
async function main() {
try { await fetchData(); }
catch (e) { console.error(e.message); }
}
main(); Section 41 of 47
Cause: infinite recursion, or recursion deeper than the V8 default of roughly 11,000 frames. Increase the limit with --stack-size, but the real fix is iterative reformulation.
The exact frame count depends on frame size, not just function calls. A recursive function with many local variables blows the stack at fewer frames than a tail-shaped function with one argument. For tree traversal on a balanced tree, 11,000 frames covers trees with 2^11,000 nodes, vastly more than any practical input. For tree traversal on a pathological linked-list-shaped tree (every node has only one child), 11,000 frames covers a 11,000-node tree, and the autograder will provide a 20,000-node test case to trip this. The reliable fix is always an explicit stack with a while loop.
RangeError: Maximum call stack size exceeded at recurse (/tmp/main.js:2:21)
// Broken: infinite recursion
function recurse(n) {
return recurse(n + 1);
}
recurse(0); // Fixed: base case
function recurse(n) {
if (n >= 10) return n;
return recurse(n + 1);
}
console.log(recurse(0)); // 10
// Or iterative for deep stacks
function loop(n) {
while (n < 100000) n++;
return n;
} Section 42 of 47
The compile step produces object files (.o on Linux, .obj on Windows). The link step combines them with libraries to produce an executable. Three classes of linker error dominate: undefined references, multiple definitions, and ABI mismatches.
The linker found a symbol declaration but no definition. Five common causes:
main.c but not helper.c. Fix: add helper.c (or helper.o) to the link line.sqrt without -lm. Fix: add -lm at the end of the gcc command (order matters; -l flags must follow the .o files that use them).int compute(int), defined int compute(long). Fix: align declaration with definition.static int helper() in helper.c, called from main.c. Fix: drop static so the symbol is exported, or move the caller into helper.c.extern "C", defined in a C source file. Fix: wrap the C header in #ifdef __cplusplus extern "C" { #endif.The linker found two definitions of the same symbol. Always caused by a definition in a header file that gets included into multiple translation units. Fix: move the definition to a .c file, leave only the declaration in the header. For C++ inline templates and constexpr definitions, the One Definition Rule allows multiple identical definitions across TUs; tag with inline or constexpr.
Linking C++ object files compiled with different standards or compilers produces undefined-reference errors with strange mangled names like __ZN4Foo3barEv. Fix: use a single compiler and matching -std= across the whole build. Mixing libstdc++ and libc++ binaries (GCC vs Clang on Linux) is a frequent source of this on student projects that include precompiled libraries.
/* Common linker fix: -lm for math */
/* main.c */
#include <math.h>
#include <stdio.h>
int main(void) {
printf("%.4f\n", sqrt(2.0));
return 0;
}
/* Compile fails without -lm:
* $ gcc main.c -o app
* /usr/bin/ld: /tmp/cc...o: undefined reference to `sqrt'
*
* Compile succeeds with -lm at the end:
* $ gcc main.c -o app -lm
*/ // extern "C" wrapper for a C header included from C++
// in c_api.h
#ifdef __cplusplus
extern "C" {
#endif
int legacy_compute(int x);
void legacy_init(void);
#ifdef __cplusplus
}
#endif
// in main.cpp
#include "c_api.h"
int main() {
legacy_init();
return legacy_compute(42);
} Section 43 of 47
Three environment-level failures cause "works locally, fails on grader" without any code change.
Windows text editors sometimes prepend a UTF-8 Byte Order Mark (0xEF 0xBB 0xBF) to source files. Most modern compilers tolerate it, but older javac and gcc versions emit cryptic syntax errors at column 1 of line 1. Fix: configure the editor to save as UTF-8 without BOM, or run sed -i '1s/^\xEF\xBB\xBF//' file.c to strip it.
Windows uses CRLF (\r\n) line endings; Unix uses LF (\n). A Bash shebang line #!/bin/bash\r fails with "bad interpreter: no such file or directory" because the kernel reads "/bin/bash\r" as the executable path. Fix: convert with dos2unix script.sh or set git's core.autocrlf=input.
German locales use comma as the decimal separator. scanf("%f", &x) on a German system reads "3,14" but rejects "3.14". Fix: call setlocale(LC_NUMERIC, "C") at program start to force the POSIX numeric locale.
Smart quotes from a copy-paste (“” instead of ""), Greek lambda from a math paste (λ instead of lambda), Cyrillic looking like Latin (а looks like a). The editor renders both the same; the compiler treats them differently. Fix: configure the editor to highlight non-ASCII characters in source files, or run cat -A file.c | grep -v '^[[:print:]]*\\$' to surface non-printable bytes.
Section 44 of 47
Default compilation accepts dubious code. Adding warning flags turns ambiguous constructs into errors, catching bug classes before they reach the autograder.
-Wall: enables the standard warnings (most missing returns, unused variables, format mismatches).-Wextra: enables additional checks not in -Wall (comparison between signed and unsigned, struct padding warnings).-Wpedantic: warn on extensions beyond the chosen standard.-Werror: treat every warning as a hard error.-fsanitize=address: AddressSanitizer instruments every memory access for OOB and use-after-free at runtime. 2x slowdown, 99% catch rate.-fsanitize=undefined: UndefinedBehaviorSanitizer catches signed overflow, null deref, alignment violations at runtime.-fsanitize=thread: ThreadSanitizer catches data races. Mutually exclusive with AddressSanitizer.-fstack-protector-strong: stack canary on every function that allocates stack arrays.-g: include DWARF debug info for GDB and core dumps.-O0 for debugging (no optimization, exact line correspondence), -O2 for release.-Xlint:all: enable every javac warning category.-Werror: promote warnings to errors.--release 17 (or your target version): enforce source and bytecode level to match runtime.-XX:+ShowCodeDetailsInExceptionMessages (default since Java 15): NPE messages name the variable.mypy --strict: static type checking on PEP 484 type hints.ruff check: lint and auto-fix common issues.python -W error: turn deprecation warnings into hard errors.pytest --strict-markers: reject unrecognized pytest markers (typo protection).# Recommended C compile line for coursework
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror \
-fsanitize=address -fsanitize=undefined \
-fstack-protector-strong \
-g -O0 main.c -o app
# Recommended C++ compile line
g++ -std=c++17 -Wall -Wextra -Wpedantic -Werror \
-fsanitize=address -fsanitize=undefined \
-g -O0 main.cpp -o app
# Java with strict warnings
javac -Xlint:all -Werror --release 17 Main.java Section 45 of 47
Debug builds (-O0) preserve every variable, do not reorder instructions, and zero-initialize stack memory in many compilers. Release builds (-O2 or -O3) aggressively reorder, eliminate dead code, inline functions, and leave stack memory uninitialized. A bug that depended on a debug-only zero will fail in release; a bug that the compiler optimized out in debug will appear in release.
-ffast-math in GCC and Clang allows the optimizer to reorder fp operations under the assumption that they are associative. Real fp is not associative; the answer changes.The standard workflow: develop in debug, but run the full test suite in release before committing. Add a CI matrix that builds both. Sanitizers (-fsanitize=address, -fsanitize=undefined) catch most undefined-behavior bugs in development before they manifest as release-only failures.
Section 46 of 47
Compilation has 3 phases. Each phase produces a different error class.
The compiler tokenizes the source (lexing) and matches tokens to grammar rules (parsing). Errors here are syntax errors: missing semicolons, mismatched braces, invalid operators. The output is an abstract syntax tree (AST). C and C++ compilers stop at the first syntax error in a translation unit if they cannot recover; Java and Python typically continue and report multiple.
The compiler walks the AST checking types, name resolution, and language-level invariants. Errors here are semantic errors: undefined variable, type mismatch, missing return, ambiguous overload. The output is an annotated AST with full type information.
The compiler emits machine code or bytecode, then the linker combines object files. Errors here are linker errors and codegen warnings: undefined references, multiple definitions, missing symbols, ABI mismatches. Java's JIT compilation happens at runtime (HotSpot, GraalVM), so what looks like a Phase 3 error in C++ surfaces at the first method call in Java.
Knowing which phase emitted an error speeds up diagnosis. A parser error is always in your source; a linker error is always in your build configuration. Skip the wrong category of fix by reading the phase tag in the error output (error: parse error vs error: undefined reference).
Section 47 of 47
The 40 errors above cover roughly 80% of failed Gradescope submissions on intro CS courses. For the other 20%, the triage workflow is the same:
-Wint-conversion, error: cannot find symbol, TypeError. The code is searchable in the standard library reference.-Wall -Werror turns warnings into errors; --release 17 vs --release 21 changes which features are accepted; python3 main.py vs python main.py can pick different interpreters.For autograder failures where the code compiles locally but fails on submission, the cause is usually version skew (Java 21 features rejected by a Java 17 grader, C++17 fold expressions rejected by C++14), file structure (Gradescope expects a specific file layout the assignment spec describes), or locale and encoding (a non-ASCII identifier you cannot see in your editor). The debugging guide walks the systematic reproduction steps; the language comparison page has the autograder-compatibility matrix per language.
More Resources
Time and space complexity for every common data structure and algorithm. Same operation shown across Java, Python, C++, and JavaScript so you can compare directly.
Open Big-O CheatsheetA repeatable 5-step process for finding bugs in C, C++, Java, Python, and JavaScript. With actual GDB session output, a Valgrind trace, and a pytest reproduction template.
Open Debugging GuideJava, Python, C++, JavaScript, C, and Assembly compared across 7 axes that matter for coursework. With autograder-compatibility notes per language.
Open Language ComparisonFAQ
; at the end of line 4 shows up as an error at line 5 column 1, pointing at the } or int that starts the next statement. Always check the line above the one the error points at.-Wall -Wextra -Werror, which promotes every warning to a hard error. Warnings exist because the compiler is unsure whether the construct is correct, and the unsureness usually reveals a real bug. The CSHH tutoring team treats unfixed warnings as failed code review; clean compiles ship.-g for debug symbols. Then run under GDB: gdb ./app, run, when it segfaults type bt for backtrace. The top frame shows the line. Valgrind also reports the line: valgrind --leak-check=full ./app and read the "Invalid read" or "Invalid write" stack trace.g++ defaults to C++20, Gradescope is C++17). Missing files or wrong file layout (the spec wants Main.java in the root, your zip nested it in a folder). Hidden non-ASCII characters (smart quotes from a paste, BOM at the file start). Compile locally with the exact flags the assignment lists.-ftemplate-backtrace-limit=0 to see the full chain. Cleaner: enable concepts (C++20) so the constraint failure is named, instead of cascading through SFINAE.-XX:+ShowCodeDetailsInExceptionMessages); Java 15 enabled them by default. Upgrade the JDK, or temporarily enable the flag on Java 14. The message names the variable that was null, which removes 90% of the guesswork.#pragma GCC diagnostic ignored "-Wunused-variable" inside the file, paired with push/pop to restore. javac: @SuppressWarnings("unchecked") on the offending declaration. Use sparingly; suppressing a warning is a confession that the code is doing something suspect but you accept the risk.if x > 0 shows up as a SyntaxError on the indented line below, because that line is no longer a valid statement in the context the parser expected.-fsanitize=undefined in development to catch it at runtime.Cheatsheets and guides cover the general ground. For your specific brief, submit it and get expert pedagogical help within 12 hours.
Submit Assignment