Skip to content

THE FIELD GUIDE

Errors and cleanup

Learn error handling with values, the question operator, fail, defer, errdefer, panic, and recover.

View source · lang/docs/guides/error-handling.md
On this page

Volang keeps errors as values and adds ?, fail, and errdefer for concise, predictable propagation and rollback.

Returning errors#

Functions that can fail place the predeclared error type last:

func parsePort(text string) (int, error) {
    value, err := strconv.Atoi(text)
    if err != nil {
        return 0, err
    }
    if value < 1 || value > 65535 {
        return 0, errors.New("port out of range")
    }
    return value, nil
}

Callers can inspect, wrap, compare, or propagate the value with the standard errors and fmt packages.

The question operator#

Apply ? to an expression whose final result is error:

func endpoint(text string) (string, error) {
    port := parsePort(text)?
    return fmt.Sprintf("127.0.0.1:%d", port), nil
}

On success, ? removes the final nil error from the expression results. On failure, it commits the error result and begins normal return unwinding. The enclosing function must itself have a final result identical to error.

An error-only expression may also use ? as a statement:

func save() error {
    validate()?
    write()?
    return nil
}

Explicit failure#

fail expression evaluates the error once and begins an error return:

func requireName(name string) error {
    if name == "" {
        fail errors.New("name is required")
    }
    return nil
}

Use fail when a branch constructs or selects the error locally. Use ? when the error already comes from a call.

Defer and rollback#

Ordinary defer runs on success, error, and panic. errdefer runs only while the function is unwinding an error or panic path:

func publish(source string) error {
    staging := createStagingDirectory()?
    errdefer os.RemoveAll(staging)

    output := os.Create(filepath.Join(staging, "release.bin"))?
    defer output.Close()

    compile(source, output)?
    activate(staging)?
    return nil
}

Deferred function values, receivers, and arguments are captured when the statement executes. Eligible deferred calls share one last-in-first-out order based on registration time.

Named results#

On propagation, current named non-error results are preserved; unnamed non-error results receive zero values. Deferred calls can observe and update named results. Error-path eligibility is decided before deferred calls run, so a later defer changing a named error does not retroactively add or remove errdefer calls.

Panic and recover#

Use error returns for expected operational failure. panic represents a broken invariant or an unrecoverable local condition. Deferred calls run while the panic unwinds; a deferred call may use recover according to the language specification.

Libraries should avoid converting routine validation, I/O, network, or user input failures into panics. Hosts should bound panic reporting and preserve a useful Volang stack trace.

Stable comparison#

Use errors.Is for sentinel-compatible comparison and errors.As for typed inspection. Wrap context with fmt.Errorf only when the formatting contract preserves the original error relationship.

Dynamic-access failures expose stable sentinels such as type mismatch and missing-member errors. Code should compare their identity instead of matching human-readable text.

Built with Volang UI · A work in progress, made to be explored.
Opening Studio…