Skip to content

[extism] Make exitOutputMaxBytes configurable via a functional option #122

@robbyt

Description

@robbyt

Summary

PR #121 (closes #94) added a formatExitOutput helper that includes the WASM plugin's output bytes in the error message returned by engines/extism/evaluator/evaluator.go:execHelper when a function returns a non-zero exit code. The output is truncated at a hard-coded exitOutputMaxBytes = 1024 constant to keep error strings readable.

The cap is right for the default case but should be configurable — hosts may want a larger window for structured plugin payloads, or a smaller one in latency-sensitive logging contexts.

Proposal

Add a functional option that flows through the same option layer as WithLogHandler / WithStaticData:

eval, err := polyscript.New[polyscript.Extism](
    polyscript.FromBytes(wasmBytes),
    polyscript.WithEntryPoint("greet"),
    polyscript.WithExitOutputMaxBytes[polyscript.Extism](8 * 1024),
)

Implementation sketch

  1. engines/extism/evaluator/evaluator.go — add exitOutputMaxBytes int field to Evaluator. Default to the current exitOutputMaxBytes value (rename the constant to defaultExitOutputMaxBytes or fold into the struct's zero-value handling). Thread the value through exec() to execHelper()'s formatExitOutput call.
  2. engines/extism/evaluator.New — accept the value (add a functional-options pattern to the evaluator if not present, or pass via constructor arg).
  3. engines/extism.WithExitOutputMaxBytes — engine-level option on the config struct in engines/extism/new.go.
  4. polyscript.WithExitOutputMaxBytes[E] — top-level generic option (or scope to Option[Extism] only since it's engine-specific).
  5. Update polyscript.go:newExtism to pass the value through.

Behavior decisions

Pick before implementing:

Tests

  • Add an option-level test in engines/extism/evaluator/evaluator_test.go covering: default (cap at 1024), custom positive value (cap at N), zero (falls back to default), negative (no cap, full output).
  • Extend TestFormatExitOutput to take the cap as a parameter (helper signature changes from formatExitOutput(output []byte) string to formatExitOutput(output []byte, maxBytes int) string once the cap is configurable).

Alternatives considered

  • Package-level var + SetExitOutputMaxBytes(n int) setter. Smallest diff but introduces global mutable state, fights t.Parallel(), and doesn't compose with the rest of the option surface. Rejected.
  • context.Context value (WithExitOutputMaxBytes(ctx, n)). Per-call rather than per-evaluator. Useful if different requests want different caps but most hosts will set this once at construction. Defer unless a real use case shows up.
  • Drop the cap entirely. Lets log infrastructure handle truncation. Simpler but a 10 MiB plugin payload then lives in the error string and any log aggregator without size limits inherits the bloat. Rejected.

Files

  • engines/extism/evaluator/evaluator.go (helper signature + Evaluator field)
  • engines/extism/evaluator/evaluator_test.go (new tests, refactor TestFormatExitOutput)
  • engines/extism/new.go (engine-level option, config field)
  • engines/extism/options.go (the engine option's godoc)
  • polyscript.go (top-level option, config field, newExtism plumbing)
  • polyscript_options_test.go (top-level test)

Depends on

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions