CUDA Cheatsheet

Error Handling

Use this CUDA reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Error Model

Every CUDA Runtime API function returns cudaError_t. Kernel launches do not return errors directly — check with cudaPeekAtLastError() or cudaGetLastError() immediately after the launch.

cudaError_t err = cudaMalloc(&d_ptr, bytes);
if (err != cudaSuccess) {
    fprintf(stderr, "cudaMalloc failed: %s\n", cudaGetErrorString(err));
    exit(EXIT_FAILURE);
}

cudaError_t — Common Values

CodeConstantMeaning
0cudaSuccessNo error
1cudaErrorInvalidValueInvalid argument
2cudaErrorMemoryAllocationOut of device memory
35cudaErrorInsufficientDriverDriver too old
98cudaErrorInvalidDeviceFunctionKernel not compiled for device
100cudaErrorNoDeviceNo CUDA-capable device found
101cudaErrorInvalidDeviceDevice index out of range
209cudaErrorNoKernelImageForDeviceNo cubin/PTX image for this SM
217cudaErrorPeerAccessUnsupportedP2P not supported
700cudaErrorIllegalAddressGlobal memory out-of-bounds access
701cudaErrorLaunchOutOfResourcesToo many registers or shared mem
702cudaErrorLaunchTimeoutKernel exceeded GPU watchdog timer
710cudaErrorAssertDevice-side assert fired
715cudaErrorIllegalInstructionInvalid instruction executed
719cudaErrorLaunchFailureUnspecified kernel launch error
999cudaErrorUnknownUnknown internal error

Error String Helpers

const char* cudaGetErrorString(cudaError_t err);   // human-readable message
const char* cudaGetErrorName(cudaError_t err);     // enum name as string

// Example:
cudaError_t e = cudaMemcpy(/*...*/);
if (e) fprintf(stderr, "[%s] %s\n", cudaGetErrorName(e), cudaGetErrorString(e));

Kernel Launch Error Checking

Kernel launches return void. Check with cudaGetLastError() (clears the error) or cudaPeekAtLastError() (does not clear).

myKernel<<<grid, block>>>(args);

// Pattern 1: check + clear immediately
cudaError_t err = cudaGetLastError();
if (err != cudaSuccess)
    fprintf(stderr, "Kernel launch error: %s\n", cudaGetErrorString(err));

// Pattern 2: also check async execution errors after sync
cudaDeviceSynchronize();
err = cudaGetLastError();
if (err != cudaSuccess)
    fprintf(stderr, "Kernel execution error: %s\n", cudaGetErrorString(err));

CUDA_CHECK Macro (standard pattern)

#define CUDA_CHECK(call)                                                      \
    do {                                                                      \
        cudaError_t _err = (call);                                            \
        if (_err != cudaSuccess) {                                            \
            fprintf(stderr, "CUDA error at %s:%d — %s (%s)\n",              \
                    __FILE__, __LINE__,                                       \
                    cudaGetErrorString(_err), cudaGetErrorName(_err));        \
            exit(EXIT_FAILURE);                                               \
        }                                                                     \
    } while (0)

// Usage
CUDA_CHECK(cudaMalloc(&d_ptr, bytes));
CUDA_CHECK(cudaMemcpy(d_ptr, h_ptr, bytes, cudaMemcpyHostToDevice));

myKernel<<<grid, block>>>(args);
CUDA_CHECK(cudaGetLastError());
CUDA_CHECK(cudaDeviceSynchronize());

Driver API Errors (CUresult)

When mixing runtime and driver API, errors are CUresult (prefix CUDA_ERROR_).

#include <cuda.h>

CUresult res = cuInit(0);
if (res != CUDA_SUCCESS) {
    const char* msg;
    cuGetErrorString(res, &msg);
    const char* name;
    cuGetErrorName(res, &name);
    fprintf(stderr, "[%s] %s\n", name, msg);
}

CUDA-GDB — Debugging on GPU

# Compile with debug info
nvcc -g -G myfile.cu -o myapp   # -G keeps device debug info

# Launch under cuda-gdb
cuda-gdb ./myapp

# Useful commands inside cuda-gdb:
# cuda thread (blockIdx.x, threadIdx.x)   — switch focus
# cuda kernel / block / warp / lane       — inspect hierarchy
# info cuda threads                        — list active threads
# info cuda kernels                        — list running kernels
# print threadIdx / blockIdx               — built-in vars

cuda-memcheck / Compute Sanitizer

# Legacy (CUDA < 11.x)
cuda-memcheck ./myapp

# Modern (CUDA 11+)
compute-sanitizer ./myapp                     # default: memcheck
compute-sanitizer --tool memcheck ./myapp     # out-of-bounds, use-after-free
compute-sanitizer --tool racecheck ./myapp    # shared memory race conditions
compute-sanitizer --tool initcheck ./myapp    # uninitialized device memory reads
compute-sanitizer --tool synccheck ./myapp    # invalid __syncthreads usage

Common output patterns:

CUDA Error: Global memory read of size 4 bytes out of bounds
 at 0x... in myKernel(float*, int) [src/main.cu:42]

NVTX — Profiler Annotations

Mark regions for Nsight Systems / Nvtx.

#include <nvtx3/nvToolsExt.h>

nvtxRangePushA("Data upload");
cudaMemcpyAsync(d_in, h_in, bytes, cudaMemcpyHostToDevice, stream);
nvtxRangePop();

nvtxRangePushA("Kernel");
myKernel<<<grid, block, 0, stream>>>(d_in, d_out, n);
nvtxRangePop();

Link with -lnvToolsExt.

Assertions in Device Code

#include <cassert>

__global__ void safeKernel(float* data, int n) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    assert(i < n && "index out of range");
    data[i] = sqrtf(data[i]);
}
// Compiles out with -DNDEBUG

When assertion fires: the kernel terminates and the next sync/API call returns cudaErrorAssert (error 710) — a sticky error.

Handling OOM Gracefully

Query available memory first with cudaMemGetInfo, and treat cudaErrorMemoryAllocation as recoverable.

size_t freeBytes, totalBytes;
cudaMemGetInfo(&freeBytes, &totalBytes);   // current free / total device memory
printf("Free: %zu MB of %zu MB\n", freeBytes >> 20, totalBytes >> 20);

float* d_ptr = nullptr;
cudaError_t err = cudaMalloc(&d_ptr, hugeBytes);
if (err == cudaErrorMemoryAllocation) {
    cudaGetLastError();   // clear the error
    // Try smaller allocation, use host, etc.
} else {
    CUDA_CHECK(err);
}

Sticky vs. Non-Sticky Errors

  • Sticky errors (cudaErrorLaunchFailure, cudaErrorIllegalAddress, etc.): contaminate the CUDA context — all subsequent API calls return the same error. Only cudaDeviceReset() clears them.
  • Non-sticky errors (e.g., cudaErrorInvalidValue): individual call fails; context is still usable. cudaGetLastError() clears them.
// After a sticky error:
cudaDeviceReset();   // destroys and resets the entire context

cudaGetLastError vs cudaPeekAtLastError

FunctionClears error?Use case
cudaGetLastError()YesCheck and consume the error
cudaPeekAtLastError()NoRead error without clearing (e.g., in a loop)
// In a loop: peek to inspect, then get to clear at the end
for (...) {
    myKernel<<<g,b>>>(args);
    if (cudaPeekAtLastError() != cudaSuccess) break;
}
cudaError_t final = cudaGetLastError();  // clear

Error Propagation in Libraries

When writing a reusable library, return errors instead of exiting:

cudaError_t myLibFunc(float* in, float* out, int n) {
    cudaError_t err;

    float* tmp;
    err = cudaMalloc(&tmp, n * sizeof(float));
    if (err != cudaSuccess) return err;

    myKernel<<<(n+255)/256, 256>>>(in, tmp, n);
    err = cudaGetLastError();
    if (err != cudaSuccess) { cudaFree(tmp); return err; }

    err = cudaMemcpy(out, tmp, n * sizeof(float), cudaMemcpyDeviceToDevice);
    cudaFree(tmp);
    return err;
}