Skip to content

Error Handling

The openpine_vm::Error enum covers both compile-time and runtime errors.

Error Types

rust
pub enum Error {
    Compile(CompileErrors),          // Parse, type-check, or compile errors
    MissingScriptType,               // No indicator()/strategy()/library() call
    InputValueNotFound(usize),       // Invalid input ID in with_input_value
    SetInputValue(usize, String),    // Type mismatch for input value
    UnsupportedTimeFrame(TimeFrame), // Script doesn't support this timeframe
    SessionNotAllowed(TradeSession), // Session type not enabled
    DataProvider(String),            // Error from the DataProvider
    Exception(Exception),            // Runtime exception (e.g. runtime.error())
    LibraryScriptNotExecutable,      // Tried to execute a library
}

Handling Errors

Pattern Matching

rust
async fn run(source: &str) {
    let mut instance = match Instance::builder(source, TimeFrame::days(1), "NASDAQ:AAPL")
        .build().await
    {
        Ok(inst) => inst,
        Err(Error::Compile(errors)) => {
            // Display compile errors with source locations
            eprintln!("{}", errors);
            return;
        }
        Err(Error::MissingScriptType) => {
            eprintln!("Script must call indicator(), strategy(), or library()");
            return;
        }
        Err(Error::Exception(exception)) => {
            // Global-scope runtime error (e.g., runtime.error() at top level)
            eprintln!("Init exception: {}", exception.display());
            return;
        }
        Err(e) => {
            eprintln!("Build error: {}", e);
            return;
        }
    };

    // Run
    match instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await {
        Ok(()) => {}
        Err(Error::Exception(exception)) => {
            eprintln!("Runtime exception: {}", exception.display());
        }
        Err(e) => {
            eprintln!("Execution error: {}", e);
        }
    }
}

Compile Errors

Compile errors include source spans for precise error reporting:

rust
match Instance::builder(source, tf, "NASDAQ:AAPL").build().await {
    Err(Error::Compile(errors)) => {
        // CompileErrors implements Display
        // Shows formatted errors with line/column numbers
        eprintln!("{}", errors);
    }
    _ => {}
}

Runtime Exceptions

Runtime exceptions occur during script execution (e.g., runtime.error() calls, index out of bounds):

rust
match instance.run_to_end("NASDAQ:AAPL", TimeFrame::days(1)).await {
    Err(Error::Exception(exception)) => {
        // Display the exception with source file context
        eprintln!("{}", exception.display());

        // Get the source span of the error
        if let Some(span) = exception.first_span() {
            eprintln!("  at line {}", span.start.line);
        }
    }
    _ => {}
}

Compilation Warnings

Warnings don't prevent execution but indicate potential issues:

rust
let instance = Instance::builder(source, tf, "NASDAQ:AAPL").build().await?;

for warning in instance.warnings() {
    eprintln!("Warning: {}", warning.display_as_warning());
}

Best Practices

  1. Always handle compile errors — show them to the user with source context
  2. Handle exceptions from build() toobuild() executes the global scope to discover script metadata (indicator(), input.*(), etc.), so any runtime error in top-level code (e.g., runtime.error(), invalid function arguments) will surface as Error::Exception before execution even begins
  3. Stop execution on runtime errors — Pine Script computes bar-by-bar with each bar depending on the previous bar's state (var variables, series history like close[1], etc.). If one bar fails, all subsequent results would be invalid, so you should stop on error
  4. Process events from the stream — log events can contain useful diagnostic info; consume the stream from run() or use run_to_end() to discard events
  5. Check warnings after build — they often indicate deprecated features or potential bugs
rust
use std::pin::pin;
use futures_util::StreamExt;

async fn run_safely(
    instance: &mut Instance,
    symbol: &str,
    timeframe: TimeFrame,
) -> Result<(), Error> {
    for w in instance.warnings() {
        eprintln!("[warn] {}", w.display_as_warning());
    }

    let mut stream = pin!(instance.run(symbol, timeframe));
    while let Some(result) = stream.next().await {
        match result? {
            Event::Log(log) => {
                eprintln!("[{:?}] {}", log.level, log.message);
            }
            Event::Alert(alert) => {
                println!("Alert: {}", alert.message);
            }
            _ => {}
        }
    }
    Ok(())
}

Next Steps

Released under the MIT License.