CUDA Cheatsheet

Error Handling

Use this CUDA reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

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;
}