ddonche/goblin-lang
0.46.24
1
0
overview docs
[[errors]]

Goblin Error Messages


Below is a comprehensive list of every Goblin error message. If you encounter an error, you should be given a link to the exact error message on this page. You can also use the sidebar links to find what you are looking for. In each entry, you'll get the error code, the name of the error message, what type it is, when it occurs, at least one example of incorrect code, and then at least one example of how to fix it. If you still have questions, feel free to ask them in the Goblin subreddit.


A0401 — unknown-action

Category: runtime / actions
When it happens: An action name is called that is not defined, exported, or built in.

Example

call("does_not_exist", 1, 2)
/// or
does_not_exist(1, 2)
call("does_not_exist", 1, 2)
/// or
does_not_exist(1, 2)

How to fix: Check the action name and imports. Define the action, or import the module that exports it.


FS0001 — filesystem-io

Category: runtime / filesystem error
When it happens: A filesystem operation fails (e.g., create_dir!, file writes/reads) due to OS-level issues such as permission denied, invalid path, missing drive, or a locked file/dir.

Example

/// Permission denied or invalid path
create_dir!("/root/protected")

/// Windows example: invalid/locked path
create_dir!("C:\Windows\System32\config\SAM")
/// Permission denied or invalid path
create_dir!("/root/protected")

/// Windows example: invalid/locked path
create_dir!("C:\Windows\System32\config\SAM")

How to fix:

  • Use a path you have permission to create or run the process with sufficient privileges.
  • Ensure the path is valid on the current OS (watch for illegal characters or reserved names).
  • If a parent directory is on an unmounted/missing volume, mount or correct the drive/path.
  • If another process holds a lock, retry after it releases the handle.
  • Prefer create_dir! with an existing/accessible parent; create_dir! is idempotent when the directory already exists.

J0001 — json-parse-failed

Category: runtime / JSON
When it happens: The string passed to json_parse (or read via read_json) is not valid JSON.

Example

json_parse("{ name: 'Alf' }")   <---- missing quotes around key, wrong quotes for strings
json_parse("{ name: 'Alf' }")   <---- missing quotes around key, wrong quotes for strings

How to fix: Provide well-formed JSON: double-quoted keys/strings, proper commas/braces.

json_parse("{ \"name\": \"Alf\" }")
json_parse("{ \"name\": \"Alf\" }")


J0002 — json-stringify-failed

Category: runtime / JSON
When it happens: Converting a value to JSON with json_stringify/json_stringify_pretty fails (e.g., value contains unsupported/recursive structures).

Example

let x = seq_new(backend: "weird");
json_stringify(x)   <---- backend not serializable to JSON
let x = seq_new(backend: "weird");
json_stringify(x)   <---- backend not serializable to JSON

How to fix: Convert or remove non-serializable parts before stringifying. Serialize only plain values (numbers, strings, arrays, maps, booleans, nil).


J0003 — json-io

Category: runtime / IO
When it happens: read_json(path) cannot read the file (not found, permissions) or similar IO errors.

Example

read_json("./missing.json")   <---- file not found
read_json("./missing.json")   <---- file not found

How to fix: Ensure the path is correct and readable. Check permissions and working directory.


J0004 — json-write-io

Category: JSON / I/O error
When it happens: Writing JSON to a file fails (e.g., missing directory, insufficient permissions, read-only path, disk full, or an invalid path string).

Example

/// Directory doesn't exist
write_json!("/no/such/dir/data.json", { a: 1 })

/// Or path isn't a string
let p = 123
write_json!(p, { a: 1 })
/// Directory doesn't exist
write_json!("/no/such/dir/data.json", { a: 1 })

/// Or path isn't a string
let p = 123
write_json!(p, { a: 1 })

How to fix:

  • Pass a valid string for the path (e.g., "out/data.json").
  • Ensure the directory exists or create it before writing.
  • Check file permissions and that the path isn't read-only.
  • Make sure there's enough disk space and the file isn't locked by another process.
  • On Windows, avoid reserved names (e.g., CON, PRN) and ensure the path is valid.

M0001 — mutation-operator-required

Category: meta / mutation required
When it happens: A mutating operation is called without the bang form (!). In Goblin, side-effecting actions (like writing files or creating directories) must use the bang form to signal intent.

Examples

/// These will raise M0001
write_json("out.json", data)
create_dir("logs/")
/// These will raise M0001
write_json("out.json", data)
create_dir("logs/")

/// Use the bang form for mutations
write_json!("out.json", data)              
create_dir!("logs/")

/// Optional pretty-print for JSON
write_json!("out.json", data, true)
/// Use the bang form for mutations
write_json!("out.json", data)              
create_dir!("logs/")

/// Optional pretty-print for JSON
write_json!("out.json", data, true)

How to fix: Use the bang (!) variant of the operation whenever it causes side effects (filesystem writes, directory creation, etc.).

Notes:

  • Non-mutating counterparts (without !) may either not exist or are intentionally disallowed to prevent accidental side effects.
  • The bang form makes intent explicit and easier to audit in code reviews.

M0002 — not-callable

Category: meta / callability error
When it happens: You try to "call" a value that isn't an action/function (e.g., a number, string, map, array, or any non-callable value).

Examples

let x = 42
x()            <---- not callable

let s = "hello"
s(1)           <---- not callable

let a = [1, 2]
a(0)           <---- not callable

# Passing a non-action as the action parameter
map([1,2,3], 5)  <---- second arg must be an action name (string)
let x = 42
x()            <---- not callable

let s = "hello"
s(1)           <---- not callable

let a = [1, 2]
a(0)           <---- not callable

# Passing a non-action as the action parameter
map([1,2,3], 5)  <---- second arg must be an action name (string)

How to fix:

  • Call only declared actions or built-ins.
  • Ensure variables you call actually refer to actions.
  • If an API expects an action name, pass it as a string, e.g. map([1,2,3], "double").
  • Refactor data (numbers/strings/maps/arrays) so they are arguments to actions, not the callee.

Correct usage

action double(n) { n * 2 }

map([1,2,3], "double")  <---- calls the action named "double"

print("hello")          <---- built-in callable
action double(n) { n * 2 }

map([1,2,3], "double")  <---- calls the action named "double"

print("hello")          <---- built-in callable


P0314 — expected-array

Category: parser / type error
When it happens: This error occurs when Goblin expects an array expression but receives a different type, such as a single value or object.

Example

sum(42)           <---- error: expected an array of expressions
sum([1, 2, 3])    <---- correct
sum(1, 2, 3)      <---- correct
sum(42)           <---- error: expected an array of expressions
sum([1, 2, 3])    <---- correct
sum(1, 2, 3)      <---- correct

How to fix: Wrap multiple values in square brackets [] to create an array. Use commas to separate elements.

Correct examples:

values = [10, 20, 30]
process(values)
values = [10, 20, 30]
process(values)


P0330 - invalid-number-literal

Category: parse
When it happens: A malformed number literal was encountered — for example, multiple decimal points or invalid characters.

Example

x = 12.3.4
x = 12.3.4

How to fix: Use valid numeric syntax:

42
3.1415
1_000_000
6.02e23
42
3.1415
1_000_000
6.02e23


P0340 — invalid-dice-notation

Category: parse
When it happens: Dice notation could not be parsed due to a missing d, malformed modifier, or unexpected character.

Example

roll("2x6")    <---- invalid — should be 2d6
roll("d20")    <---- invalid — missing count before d
roll("2d6++")  <---- invalid modifier
roll("2x6")    <---- invalid — should be 2d6
roll("d20")    <---- invalid — missing count before d
roll("2d6++")  <---- invalid modifier

How to fix: Ensure dice notation follows valid forms such as:

2d6           
4d10+2         
3d8-1
2d20+adv
5d6k3
1d500
3d12!1
2d6           
4d10+2         
3d8-1
2d20+adv
5d6k3
1d500
3d12!1


P0341 — dice-notation-invalid

Category: parser
When it happens: The string passed to roll_str or roll_detail_str is not valid dice notation.

Examples

roll_str("2d")          <---- missing sides
roll_str("d6")          <---- missing count
roll_str("2x6")         <---- unknown delimiter
roll_detail_str("")     <---- empty string
roll_str("2d")          <---- missing sides
roll_str("d6")          <---- missing count
roll_str("2x6")         <---- unknown delimiter
roll_detail_str("")     <---- empty string

Valid forms (examples):

  • "1d20"
  • "3d6+2"
  • "4d8-1"
  • "2d10k1" (keep highest 1)
  • "5d6!2" (drop lowest 2)

How to fix: Ensure the dice string matches a supported pattern (e.g., NdS, optionally with modifiers like +N, -N, khX, klX, dhX, dlX).

Help: Provide a non-empty string using recognized dice notation, then call roll_str or roll_detail_str with that value.


P0802 — lvalue-expected

Category: parser error
When it happens: The parser expected a valid lvalue — a variable name or a field access — but found a different kind of expression.

Examples

/// invalid: left side is not a variable/field
set (a + b) = 3

/// invalid: not a name or field access
set 42 = x
/// invalid: left side is not a variable/field
set (a + b) = 3

/// invalid: not a name or field access
set 42 = x

How to fix: Use a plain identifier or a field access as the target.

total = a + b
user >> name = "Ada"
total = a + b
user >> name = "Ada"


P0804 — field-name-required

Category: parser error
When it happens: A field name (identifier) is required immediately after the >> operator, but something else (or nothing) is provided.

Example

>> 123      <---- not an identifier
>> "name"   <---- string, not an identifier
>>          <---- missing field name
>> 123      <---- not an identifier
>> "name"   <---- string, not an identifier
>>          <---- missing field name

How to fix: Provide a valid identifier after >>.

>> name          
>> next_field
>> name          
>> next_field


P1408 — pick-missing-source

Category: parse/config error
When it happens: A pick call is made without specifying a source collection (from <collection> / src) or a numeric form (e.g., range_start/range_end or digits). The runtime cannot determine what to pick from.

Example

/// No source and no numeric range/digits
pick 

/// Has count but missing 'src' and numeric config
pick 1
/// No source and no numeric range/digits
pick 

/// Has count but missing 'src' and numeric config
pick 1

How to fix: Provide either:

  • a collection source via src, e.g. an array/seq/map, or
  • a numeric configuration via range_start/range_end

Correct examples

/// From a collection
pick 2 from ["red", "green", "blue"]

/// From a numeric range (exclusive upper bound)
pick 1 from 1..5    <---- pick 1 number between 1 and 4

/// From a numeric range (inclusive upper bound)
pick 1 from 1...5   <---- pick 1 number between 1 and 5

/// Generate d-digit numbers
pick 1_4            <---- pick 1 4-digit number
/// From a collection
pick 2 from ["red", "green", "blue"]

/// From a numeric range (exclusive upper bound)
pick 1 from 1..5    <---- pick 1 number between 1 and 4

/// From a numeric range (inclusive upper bound)
pick 1 from 1...5   <---- pick 1 number between 1 and 5

/// Generate d-digit numbers
pick 1_4            <---- pick 1 4-digit number

Help: Decide what domain you want to sample from. Use src for collections, or range_*/digits for numbers.


P9001 — readonly-field

Category: parser / immutability rule
When it happens: You attempt to modify a field that is marked as read-only (immutable) in the type or record definition.

Examples

user42|User = { id!: 42, name: "Sam" }    <---- assume `id` is readonly
user42.id = 7                             <---- P9001: cannot modify readonly field 'id'
user42|User = { id!: 42, name: "Sam" }    <---- assume `id` is readonly
user42.id = 7                             <---- P9001: cannot modify readonly field 'id'

/// Inside a pipeline/update where `status` is readonly
order >> status = "shipped"        <---- P9001
/// Inside a pipeline/update where `status` is readonly
order >> status = "shipped"        <---- P9001

How to fix:

  • Don't assign to the readonly field. Update a different, mutable field.
  • Construct a new value instead of mutating:

user7|User = { id: 7, name: "Sara"}
user7|User = { id: 7, name: "Sara"}

  • If the field shouldn't be readonly, change the definition (remove the readonly qualifier) in the type/module that declares it.

Message text: cannot modify readonly field '<id>'


R0000 — not-implemented

Category: internal-error
When it happens: A feature or function has not yet been implemented.

Example

future_magic_feature()
future_magic_feature()

How to fix: Check Goblin's release notes or documentation for upcoming implementations.


R0009 — internal-assign-slot

Category: internal error
When it happens: This occurs when Goblin cannot locate an existing variable slot during an assignment operation. It indicates a logic or state error inside the interpreter.

Example

/// Internal runtime error example (should not occur in normal Goblin code)
/// Internal runtime error example (should not occur in normal Goblin code)

How to fix: This is not user error. Report the issue with the source file and stack trace to the Goblin maintainers for investigation.


R0101 — unknown-ident

Category: runtime / name resolution error
When it happens: You reference a variable or name that hasn't been declared/bound in the current scope (and isn't in any outer scope or module export).

Examples

say foo            <---- 'foo' was never declared

x = 10
say y              <---- 'y' does not exist

act f() => say z   <---- is unknown inside f
say foo            <---- 'foo' was never declared

x = 10
say y              <---- 'y' does not exist

act f() => say z   <---- is unknown inside f

How to fix:

  • Declare/bind the variable before using it.
  • Check for typos (e.g., fo0 vs foo).
  • Ensure the name is in scope (import or qualify it if it lives in a module). (If the name is not in scope, you will get a warning to shadow.)

Fixed examples

foo = 42
say foo     <---- ok
foo = 42
say foo     <---- ok

import game/hero as h
h::generate_hero()    <---- qualify with module name/alias if you need to use an action from the module
import game/hero as h
h::generate_hero()    <---- qualify with module name/alias if you need to use an action from the module


R0111 — duplicate-local

Category: runtime / binding error
When it happens: Triggered when attempting to declare a variable that already exists in the current scope using the shadow operator incorrectly.

Example

x = 5
x [= 10  <---- error: 'x' already declared in this scope
x = 5
x [= 10  <---- error: 'x' already declared in this scope

How to fix: Use = to reassign an existing variable or choose a new name. If you intend to shadow the variable, use [= in a different scope, not the same one.


R0113 — immutable-assign

Category: runtime / assignment error
When it happens: Occurs when trying to reassign a value that was declared immutable using imm.

Example

imm x = 42
x = 10  <---- error: cannot reassign immutable 'x'
imm x = 42
x = 10  <---- error: cannot reassign immutable 'x'

How to fix: Remove the imm keyword if you want to allow reassignment, or create a new variable with a different name.


R0114 — outer-scope-shadow

Category: runtime / scope error
When it happens: Raised when you try to declare a new variable in a local scope that already exists in an outer scope, without explicitly shadowing it.

Example

x = 1
act test
  x = 2  <---- error: 'x' exists in an outer scope; use '[=' to shadow
end
x = 1
act test
  x = 2  <---- error: 'x' exists in an outer scope; use '[=' to shadow
end

How to fix: If you intend to create a new local variable, use [= to shadow the outer one. Otherwise, reassign the existing one with =.


R0115 — namespace-not-found

Category: runtime / name-resolution error
When it happens: You use the namespace operator :: with an identifier that isn't a declared module or enum in scope. The engine can't resolve the left-hand side as a valid namespace.

Examples

/// 'Math' was never declared as a module or enum
x = Math::sqrt(9)

/// 'Color' exists as a value, not as an enum type
y = Color::Red

/// Misspelled module name
import game/hero as h
s = he::title("hi")  <---- typo: 'h'
/// 'Math' was never declared as a module or enum
x = Math::sqrt(9)

/// 'Color' exists as a value, not as an enum type
y = Color::Red

/// Misspelled module name
import game/hero as h
s = he::title("hi")  <---- typo: 'h'

How to fix:

  • Make sure the left side of :: is a declared module or enum that's in scope.
  • Import it (import foo/bar) or fully qualify the path.
  • If it's an enum variant you want, reference the correct enum type.
  • Check for typos.

Corrected

/// Module exists
import data/locations
x = locations::get_location(location)

/// Proper enum type
enum Color { Red, Green }
y = Color::Red
/// Module exists
import data/locations
x = locations::get_location(location)

/// Proper enum type
enum Color { Red, Green }
y = Color::Red


R0116 — unknown-class

Category: runtime
When it happens: Code references a class name that isn't defined or registered in the current session (e.g., typo in the type tag, missing import/registration, or the class wasn't declared with @ClassName = ...).

Example

/// Typo: 'Usr' instead of 'User'
alice|Usr = { name: "Alice", email: "alice@example.com" }
/// Typo: 'Usr' instead of 'User'
alice|Usr = { name: "Alice", email: "alice@example.com" }

How to fix: Declare the class, then use the correct name.

@User =
    name: {name}
    email: {email}
end

alice|User = { name: "Alice", email: "alice@example.com" }
@User =
    name: {name}
    email: {email}
end

alice|User = { name: "Alice", email: "alice@example.com" }

Also check:

  • Spelling of the class name in type tags (e.g., bob|User).
  • That the module defining the class was imported/loaded before use.
  • Build/registration order if classes are registered programmatically.

R0117 — unknown-enum

Category: runtime / name resolution
When it happens: Code references an enum type that is not declared, not imported, or out of scope.

Example

x = Color::Red  <---- 'Color' enum was never declared/imported
x = Color::Red  <---- 'Color' enum was never declared/imported

How to fix:

  • Declare the enum before use, or import it from the module where it is defined.
  • Check for typos in the enum name.
  • Ensure the current module has visibility to the enum (exports/imports).

R0118 — unknown-variant

Category: runtime / name resolution
When it happens: The enum exists, but the referenced variant name does not exist on that enum.

Example

enum Color { Red, Green }
x = Color::Blue   <---- 'Blue' is not a variant of 'Color'
enum Color { Red, Green }
x = Color::Blue   <---- 'Blue' is not a variant of 'Color'

How to fix:

  • Use one of the defined variants of the enum.
  • Add the missing variant to the enum definition if it's intended to exist.
  • Check for typos or wrong enum/variant pairing.

R0200 — numeric-expected

Category: type-error
When it happens: A numeric value was required, but another type was provided.

Example

big("hello")
big("hello")

How to fix: Pass a valid numeric type (int, float, big, or pct) instead of non-numeric data.


R0201 — type-mismatch-runtime

Category: type-error
When it happens: A numeric value was expected, but a non-numeric type was provided at runtime.

Example

sqrt("hello")
sqrt("hello")

How to fix: Provide a numeric value such as int, float, big, or pct.


R0203 — big-exponent-range

Category: runtime / math error
When it happens: pow(base, exp) uses Big math and the exponent is a Big integer that cannot fit into a signed 64‑bit integer.

Example

pow(big(2), big(10_000_000_000_000))  <---- exponent too large for integer Big pow
pow(big(2), big(10_000_000_000_000))  <---- exponent too large for integer Big pow

How to fix: Reduce the exponent to a 64‑bit range, or switch to floating‑point exponentiation when non‑integer or very large exponents are needed.


R0206 — division-by-zero

Category: math-error
When it happens: Division by zero was attempted, or zero was used as the base of a negative exponent.

Example

0 / -1
0 / -1

How to fix: Avoid dividing by zero or using zero as a base in negative powers.


R0207 — math-domain

Category: runtime / math error
When it happens: Invalid numeric domain, e.g., calling sqrt() on a negative value.

Example

sqrt(-1)      <---- error: cannot take square root of a negative value
sqrt(-1)      <---- error: cannot take square root of a negative value

How to fix: Validate inputs before calling the function (e.g., ensure non‑negative for sqrt). Use abs() or conditional checks as appropriate.


R0212 — invalid-cast-big

Category: invalid-cast
When it happens: A value cannot be safely converted to a decimal (Big).

Example

big(true)
big(true)

How to fix: Use a valid numeric or numeric string such as big("0.1") or big(1.25).


R0223 — percent-string-big

Category: invalid-string
When it happens: A string with a trailing '%' was passed to big().

Example

big("12.5%")
big("12.5%")

How to fix: Use pct(12.5) instead, or remove % and scale manually before conversion.


R0224 — invalid-numeric-string-big

Category: invalid-string
When it happens: big() received a string that is not a valid decimal number.

Example

big("12.3.4")
big("abc")
big("12.3.4")
big("abc")

How to fix: Provide a valid numeric string like big(1234.50). Digits with one decimal point are allowed; underscores are ignored.


R0298 — float-to-decimal-failed

Category: conversion-error
When it happens: Conversion from Float or Percent to Decimal failed due to overflow or invalid representation.

Example

big(1e400)
big(1e400)

How to fix: Use smaller, representable numbers when converting to Big (Decimal).


R0301 — wrong-arity

Category: call-error
When it happens: A function or action was called with the wrong number of arguments.

Example

some_action(1, 2, 3)  <---- expected other than 3 arguments
some_action(1, 2, 3)  <---- expected other than 3 arguments

How to fix: Provide the correct number of parameters expected by the action.


R0302 — missing-argument

Category: runtime / call error
When it happens: Anaction is called without providing all required positional arguments, and no default value exists for the missing parameter.

Example

act greet(name, title)
  say "Hello {title} {name}!"
end

/// Missing 'title'
greet("Alice")
act greet(name, title)
  say "Hello {title} {name}!"
end

/// Missing 'title'
greet("Alice")

How to fix: Provide all required arguments when calling the action or define default values for optional parameters.


R0313 — precision-loss-float

Category: precision-error
When it happens: A value too large (≥ 2⁵³) or non-finite decimal cannot be safely represented as a float.

Example

float(99999999999999999)
float(99999999999999999)

How to fix: Use big() for exact precision, or round/truncate before conversion.


R0314 — invalid-cast-float

Category: invalid-cast
When it happens: A string or value cannot be parsed or converted into a valid float.

Example

float(12.5%)
float("abc")
float(12.5%)
float("abc")

How to fix: Use pct(12.5) or 12.5% for percentages or pass a valid numeric.


R0316 — invalid-cast-int

Category: invalid-cast
When it happens: A value cannot be cast to an integer (non-numeric or invalid string).

Example

int("hello")
int("hello")

How to fix: Pass a valid numeric or numeric-string value such as int(42) or int("123").


R0320 — invalid-big-to-pct

Category: invalid-cast
When it happens: The value passed to pct() is a Big number that cannot be represented as a float.

Example

pct(1e400)
pct(1e400)

How to fix: Use smaller or representable numeric values. For extremely large decimals, consider scaling manually.


R0321 — invalid-pct-string

Category: invalid-string
When it happens: The string passed to pct() is not a valid numeric or percent string.

Example

pct("abc%")
pct("abc%")

How to fix: Use a valid numeric string such as pct("12.5") or pct("75").


R0322 — invalid-pct-cast

Category: invalid-cast
When it happens: The value cannot be cast to a percent (unsupported type).

Example

pct(true)
pct(true)

How to fix: Only numeric, percent, or string values can be used with pct(). Convert other types before calling.


R0325 — non-finite-float

Category: math-error
When it happens: A non-finite float (NaN or Infinity) was encountered during a Big (Decimal) math operation.

Example

big(1/0)
big(1/0)

How to fix: Ensure the value is finite before performing Big math operations.


R0326 — big-overflow

Category: conversion-error
When it happens: A Big (Decimal) number was too large or precise to fit into a 64-bit float.

Example

float(999999999999999999999999999999999999999)
float(999999999999999999999999999999999999999)

How to fix: Use Big math or reduce the number's precision before converting to float.


R0401 — invalid-index

Category: type-error
When it happens: A non-integer or negative value is provided where a non-negative integer index is expected.

Example

arr = [10, 20, 30]
arr[-1]      <---- invalid — negative index
arr[2.5]     <---- invalid — fractional index
arr["one"]   <---- invalid — string index
arr = [10, 20, 30]
arr[-1]      <---- invalid — negative index
arr[2.5]     <---- invalid — fractional index
arr["one"]   <---- invalid — string index


R0402 — array-expected

Category: runtime / collection error
When it happens: A builtin expecting a single array argument (e.g., sum, avg, min/max in single-argument form) receives a non-array value.

Example

avg(42)           <---- error: 42 is not an array
min(1)            <---- error: single-arg form expects an array
avg(42)           <---- error: 42 is not an array
min(1)            <---- error: single-arg form expects an array

How to fix: Wrap values in an array and pass that array as the single argument.

Correct examples:

sum([1, 2, 3])
avg([10, 20, 30])
min([x, y, z])
sum([1, 2, 3])
avg([10, 20, 30])
min([x, y, z])


R0403 — no-such-field

Category: object-error
When it happens: Attempting to access a field or member that doesn't exist.

Example

user >> age
user >> age

How to fix: Ensure the field name exists in the object's class definition.


R0404 — empty-array

Category: runtime / collection error
When it happens: min([..]) or max([..]) is called with an empty array.

Example

min([])  <---- error: min of empty array
max([])  <---- error: max of empty array
min([])  <---- error: min of empty array
max([])  <---- error: max of empty array

How to fix: Ensure the array has at least one element before calling min/max, or handle the empty case separately.


R0405 — loop-control-outside-loop

Category: runtime / control-flow error
When it happens: skip or stop is executed while not inside a looping construct.

Examples

/// not inside a loop
skip
/// not inside a loop
skip

/// also not inside a loop
stop
/// also not inside a loop
stop

How to fix: Wrap the control statement inside a loop (while, for, or repeat), or remove it.

repeat 3
  if x < 0 
    skip 
    say x
  end
end
repeat 3
  if x < 0 
    skip 
    say x
  end
end

while x < 10
  if x == 5 
    stop
  end
  x = x + 1
end
while x < 10
  if x == 5 
    stop
  end
  x = x + 1
end

Help: Use skip to continue to the next iteration and stop to exit the current loop—but only from within a loop body.


R0410 — malformed-bound-action

Category: runtime / object method binding
When it happens: The runtime encounters a "bound action" (method wrapper) map that is missing the required name string field. This usually means a bound method object was constructed incorrectly or corrupted before use.

Example

m = { kind: "bound_action" }   /// missing name
call(m)                        <---- → R0410
m = { kind: "bound_action" }   /// missing name
call(m)                        <---- → R0410

How to fix:

  • Don't hand-craft bound action objects; let the language create them via normal member access (e.g., obj.method).
  • If you're generating or transforming these objects, ensure they include a string name field (and other required fields) before calling.

Notes: Bound actions are internal wrappers that capture a receiver and the method name for later invocation. Missing or mistyped fields make the wrapper invalid.


R0411 — relation-value-required

Category: runtime
When it happens: An of relation field (foreign key) is required during object construction, but no value was provided and the field is not nullable.

Example

@Post =
    title: Str
    of User as author   <---- non-nullable relation
end

/// Missing `author` value
p|Post = { title: "Hello" }
@Post =
    title: Str
    of User as author   <---- non-nullable relation
end

/// Missing `author` value
p|Post = { title: "Hello" }
How to fix: Provide a related object or its foreign key.

u|User = { name: "Alice", email: "alice@example.com" }
p|Post = { title: "Hello", author: u }        <---- object with id
/// or
p|Post = { title: "Hello", author: u >> id }  <---- explicit id (string)
u|User = { name: "Alice", email: "alice@example.com" }
p|Post = { title: "Hello", author: u }        <---- object with id
/// or
p|Post = { title: "Hello", author: u >> id }  <---- explicit id (string)


R0500 — unclosed-interp-brace

Category: parse / interpolation-error
When it happens: This occurs when an interpolated string (one containing {}) starts with a { but does not contain a matching closing brace }. It can also appear when braces are malformed or left unbalanced.

Example

say "Hello {name"
///              ^ missing closing '}'
say "Hello {name"
///              ^ missing closing '}'

How to fix: Add a closing brace } to complete the interpolation, or escape literal braces using double braces {{ or }} if you intend to print them literally.

Correct examples:

say "Hello {name}"        <---- interpolate variable
say "Use {{ to print {"   <---- literal brace
say "Hello {name}"        <---- interpolate variable
say "Use {{ to print {"   <---- literal brace


R0501 — import-io

Category: runtime / I/O error
When it happens: This occurs when Goblin cannot determine or access the current working directory during an import. This may happen due to restricted permissions, a missing directory, or a sandboxed environment.

Example

import utils
///     ^ error: cannot get current directory
import utils
///     ^ error: cannot get current directory

How to fix: Ensure that the working directory exists and is readable. If running in a container, daemon, or restricted environment, verify that the process has permission to access the filesystem.

Correct examples:

cd /project/root
/// then run goblin normally
cd /project/root
/// then run goblin normally


R0502 — import-failed

Category: runtime / module resolution
When it happens: This error indicates that Goblin attempted to import a module or symbol but could not resolve or read it. This may be due to an incorrect path, missing file, or inaccessible module.

Example

import non_existent_module
///    ^ module cannot be resolved
import non_existent_module
///    ^ module cannot be resolved

How to fix: Check that the specified file or module exists and is accessible. Use absolute or correct relative paths. When importing named items, ensure they are exported from the target module.

Correct examples:

import data/helpers
import math as m
import parser as p
import data/helpers
import math as m
import parser as p


R0503 — op-not-meaningful

Category: runtime / collection error
When it happens: An operation is used that has no defined meaning for the target collection (e.g., put first on a map, which has no inherent order).

Example

a = {d: 1, e: 2}
put_first!(a, 3)   <---- maps don't have an ordered 'first'
a = {d: 1, e: 2}
put_first!(a, 3)   <---- maps don't have an ordered 'first'

How to fix: Use operations that make sense for the type of collection. For maps, use key-based access such as at("key") instead of positional operations.


R0504 — op-not-implemented

Category: runtime / limitation
When it happens: The operation/position combination exists conceptually but hasn't been implemented yet (e.g., update where on maps).

Example

update_where!(is_even) {a:1, b:2} with 0  <---- not yet implemented for maps
update_where!(is_even) {a:1, b:2} with 0  <---- not yet implemented for maps

How to fix: Rewrite the logic using supported operations (like filtering and rebuilding), or wait for the feature to be implemented in a later version.


R0505 — op-not-supported

Category: runtime / operation error
When it happens: The combination of collection type, selector, and operation is unsupported (e.g., update all on strings).

Example

x = "abc"
update_all!(x, "x")         <---- unsupported operation for strings; this actually does work, though
x = "abc"
update_all!(x, "x")         <---- unsupported operation for strings; this actually does work, though

How to fix: Use only supported operations: grab, put, update, or delete with positions like first, last, at(i), where(pred), or all.


R0701 — empty-collection

Category: runtime / collection error
When it happens: An operation requiring elements is used on an empty collection (e.g., grab first on an empty array or string).

Example

b = {}
grab_first(b)
b = {}
grab_first(b)

How to fix: Ensure the collection is non-empty before performing the operation, or handle the empty case explicitly using conditional logic.


R0702 — invalid-range-no-values

Category: runtime / range filtering error
When it happens: A numeric range combined with filters (e.g., digits, unique, or exclusivity) leaves no valid values to choose from. This typically occurs in functions like pick when the specified interval is empty or the constraints exclude every candidate.

Examples

/// Empty interval (exclusive end by default): 5..5 ⇒ no values
pick 1 from 5..5

/// Filters eliminate all values: requires 1-digit numbers in a two-digit range
pick 1_2 from 0...9

/// Too strict digit-uniqueness for a tiny range
pick 3 from 100..102 unique
/// Empty interval (exclusive end by default): 5..5 ⇒ no values
pick 1 from 5..5

/// Filters eliminate all values: requires 1-digit numbers in a two-digit range
pick 1_2 from 0...9

/// Too strict digit-uniqueness for a tiny range
pick 3 from 100..102 unique

How to fix:

  • Widen the range or enable inclusion when appropriate.
  • Loosen filters: reduce digits, don't use unique or remove conflicting constraints.
  • Provide a different source (e.g., from <collection>) that actually contains eligible values.

R0703 — insufficient-distinct

Category: runtime / sampling error
When it happens: A request asks for more distinct items than exist in the collection or domain when duplicates are not allowed.

Examples

/// Not enough unique elements in the source array
pick 4 from ["a", "b", "c"] without dups
pick 3 from ["Sam", "Frodo"]

/// Not enough unique values in range 1..3 when 4 are requested without replacement
pick 4 from 1...3 without dups
/// Not enough unique elements in the source array
pick 4 from ["a", "b", "c"] without dups
pick 3 from ["Sam", "Frodo"]

/// Not enough unique values in range 1..3 when 4 are requested without replacement
pick 4 from 1...3 without dups

Why it fails: Without replacement (allow_dups: false), the maximum number of items you can draw equals the size of the source set. Asking for more would require duplicates, which are disallowed.

How to fix:

  • Reduce count to be ≤ the number of available unique items, or
  • Increase the size of the source (add more elements / widen the range), or
  • Permit duplicates by setting allow_dups: true.

Corrected

/// Reduce count
pick 3 from ["a", "b", "c"] without dups

/// Or allow duplicates
pick 3 from ["Sam", "Frodo"] with dups

/// Or widen the range
pick 4 from 1...5 without dups
/// Reduce count
pick 3 from ["a", "b", "c"] without dups

/// Or allow duplicates
pick 3 from ["Sam", "Frodo"] with dups

/// Or widen the range
pick 4 from 1...5 without dups


R0704 — sample-too-large

Category: runtime / collection sampling
When it happens: A sampling operation without replacement requests more items than exist in the source collection (e.g., pick/reap with without dups and count larger than the collection size).

Examples

/// not enough numbers in that range
pick 4 from 1...3 without dups        <---- sample-too-large: requested sample of 4 exceeds available 3 distinct values in range
/// not enough numbers in that range
pick 4 from 1...3 without dups        <---- sample-too-large: requested sample of 4 exceeds available 3 distinct values in range

/// Map (entries)
m = { a: 1, b: 2 }
pick 3 from m wo dups                 <---- sample-too-large: requested 3 entries but only 2 available
/// Map (entries)
m = { a: 1, b: 2 }
pick 3 from m wo dups                 <---- sample-too-large: requested 3 entries but only 2 available

How to fix:

  • Reduce count so it is ≤ the number of available items/entries when using without dups or wo dups.
  • Or set with dups to allow sampling with replacement.

Help:

  • Use len(collection) to check availability before sampling.
  • For maps, remember that availability is the number of key—value pairs (entries).

R0705 — single-die-required

Category: runtime / dice config error
When it happens: adv (advantage) or dis (disadvantage) is used with a roll where count ≠ 1.

Example

roll 3d20 +adv      <---- error: R0705: single-die-required: adv/dis requires a single die (count=1)
roll 3d20 +adv      <---- error: R0705: single-die-required: adv/dis requires a single die (count=1)

How to fix:

  • Use exactly one die when enabling adv or dis.
  • Or remove adv/dis when rolling multiple dice.

roll 1d20 +adv
roll 3d20
roll 1d20 +adv
roll 3d20


R0706 — invalid-option-combination

Category: runtime / dice config error
When it happens: Mutually exclusive options are provided together, e.g. keep_high and drop_low at the same time.

Example

roll 4d6k3x1
roll 4d6k3x1

How to fix:

  • Choose only one of the conflicting options.

roll 4d6k3
roll 4d6x1
roll 4d6k3
roll 4d6x1


R0707 — reroll-eq-out-of-range

Category: runtime / dice config error
When it happens: reroll_eq is set to a face not possible for the die (e.g., outside 1..=sides).

Example

roll 2d6r0          <---- error: R0707: reroll-eq-out-of-range: reroll_eq must be between 1 and sides
roll 2d6r7
roll 2d6r0          <---- error: R0707: reroll-eq-out-of-range: reroll_eq must be between 1 and sides
roll 2d6r7

How to fix:

  • Set reroll_eq to a value between 1 and sides inclusive.

roll 2d6r1
roll 2d6r1


R0708 — weights-len-mismatch

Category: runtime / weighted sampling error
When it happens: The length of weights does not match the length of the source collection passed to sample_weighted.

Example

sample_weighted({ src: ["A", "B", "C"], weights: [0.5, 0.5], count: 1 })        <---- error: R0708: length-mismatch: ‘weights’ length must match ‘src’.
sample_weighted({ src: ["A", "B", "C"], weights: [0.5, 0.5], count: 1 })        <---- error: R0708: length-mismatch: ‘weights’ length must match ‘src’.

How to fix:

  • Provide one weight per item in src.
  • Ensure both src and weights are non-empty and aligned by index.

/// OK: same length
sample_weighted({ src: ["A", "B", "C"], weights: [0.2, 0.3, 0.5], count: 1 })
/// OK: same length
sample_weighted({ src: ["A", "B", "C"], weights: [0.2, 0.3, 0.5], count: 1 })

Notes:

  • weights must be numbers ≥ 0 and their total must be > 0 (separate validations).

R0902 — no-result

Category: no-result
When it happens: The REPL or evaluator finished without a final expression value to return.

Example

/// (empty line)
/// or only statements that don't yield a value
/// (empty line)
/// or only statements that don't yield a value

How to fix: Enter an expression that yields a value (e.g., 2 + 2), or assign then echo (x = 5 then x).


R0903 — return-arity-mismatch

Category: runtime / return value arity
When it happens: A call is used in a multi-target assignment (or destructuring) but the function returns a single value instead of the required number of values.

Example

/// Expects two values on the left side
(x, y) = compute()
///      ^^^^^^^ returns only one value
/// Expects two values on the left side
(x, y) = compute()
///      ^^^^^^^ returns only one value

How to fix:

  • Make the function return exactly the number of values expected by the assignment; or
  • Adjust the assignment to match what the function returns.

Corrected examples

# Option 1: return two values
action compute() {
  stop (1, 2)
}
let (x, y) = compute()
# Option 1: return two values
action compute() {
  stop (1, 2)
}
let (x, y) = compute()

# Option 2: match a single return value
action compute() { stop 42 }
let x = compute()
# Option 2: match a single return value
action compute() { stop 42 }
let x = compute()


T0202 — positive-int-expected

Category: type / runtime error
When it happens: A non-positive or non-integer value is supplied where a positive integer (≥ 1) is required (e.g., configuration fields like counts, sizes, or digits).

Example

# 'count' must be a positive integer
pick { count: 0 }

# non-integer
pick { count: 2.5 }

# other places that require positive integers
roll { count: -3, sides: 6 }
format(n, -1)
# 'count' must be a positive integer
pick { count: 0 }

# non-integer
pick { count: 2.5 }

# other places that require positive integers
roll { count: -3, sides: 6 }
format(n, -1)

How to fix: Provide an integer ≥ 1.

pick { count: 3 }
roll { count: 2, sides: 6 }
format(n, 2)
pick { count: 3 }
roll { count: 2, sides: 6 }
format(n, 2)

Help: Ensure the value is an integer and greater than or equal to 1. If you derived it from an expression, clamp or cast appropriately (e.g., max(1, x.i())).


T0203 — boolean-expected

Category: type / runtime error
When it happens: A non-boolean value (such as a number, string, or object) is used where a boolean (true or false) is required.

Example

if 5 {
    say "hello"
}
if 5 {
    say "hello"
}

How to fix: Ensure the value is a proper boolean or an expression that evaluates to one.

if true {
    say "hello"
}

# or
if x > 5 {
    say "x is greater than 5"
}
if true {
    say "hello"
}

# or
if x > 5 {
    say "x is greater than 5"
}

Help: Use true or false, or an expression that returns a boolean value.


T0204 — integer-expected

Category: type / runtime error
When it happens: A non-integer (e.g., a float like 3.5 or a string that doesn't parse to an integer) is provided where a whole number is required.

Typical trigger: Range bounds or similar parameters that must be whole numbers, e.g. range_start, range_end.

Example

pick({ range_start: 1.2, range_end: 10 })
#              ^ not an integer
pick({ range_start: 1.2, range_end: 10 })
#              ^ not an integer

How to fix: Provide whole numbers (integers) for the parameter.

pick({ range_start: 1, range_end: 10 })
pick({ range_start: 1, range_end: 10 })

Help: Use whole numbers for parameters that require integers (e.g., 1, 42, -7). Avoid decimals like 3.14 or strings that aren't clean integers.


T0205 — type-mismatch

Category: type-mismatch
When it happens: A value of the wrong type is used where a specific type (string or character) is expected.

Example

say(123)         # expects a string
grade = "AB"     # expects a single char, not a multi-character string
say(123)         # expects a string
grade = "AB"     # expects a single char, not a multi-character string


T0206 — non-nullable-field-required

Category: type
When it happens: A non-nullable field without a default wasn't provided during construction (named or positional).

Example

@User =
    name: Str          # required
    email: Str?        # nullable
end

u|User = { email: "a@example.com" }  # missing `name`
@User =
    name: Str          # required
    email: Str?        # nullable
end

u|User = { email: "a@example.com" }  # missing `name`

Error output

error: T0206: non-nullable-field-required: non-nullable field requires a value
  └─ main.gbln:7:1
7 | u|User = { email: "a@example.com" }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  = help: Field 'name' is required and has no default.
  = help: Provide a value explicitly or add a default.
  = link: https://goblinlang.org/docs/errors#T0206

How to fix: Provide the field or define a default.

u|User = { name: "Alice", email: "a@example.com" }
# or
@User =
    name: Str = "Anon"
    email: Str?
end
u|User = { name: "Alice", email: "a@example.com" }
# or
@User =
    name: Str = "Anon"
    email: Str?
end


T0207 — non-nullable-field-nil

Category: type
When it happens: nil was provided for a non-nullable field during construction.

Example

@User =
    name: Str      # non-nullable
    bio: Str?
end

u|User = { name: nil }    # invalid: name is non-nullable
@User =
    name: Str      # non-nullable
    bio: Str?
end

u|User = { name: nil }    # invalid: name is non-nullable

Error output

error: T0207: non-nullable-field-nil: cannot assign nil to non-nullable field
  └─ main.gbln:7:1
7 | u|User = { name: nil }
    | ^^^^^^^^^^^^^^^^^^^^
  = help: Field 'name' is non-nullable.
  = help: Provide a non-nil value or mark the field as nullable.
  = link: https://goblinlang.org/docs/errors#T0207

How to fix: Provide a value or change the field to nullable (Str?).

u|User = { name: "Alice" }
u|User = { name: "Alice" }


T0208 — object-or-array-expected

Category: type
When it happens: The right-hand side of object construction was not a map ({...}) or array ([...]).

Example

@User =
    name: Str
end

u|User = 42          # invalid construction
@User =
    name: Str
end

u|User = 42          # invalid construction

Error output

error: T0208: object-or-array-expected: object or array expected
  └─ main.gbln:6:1
6 | u|User = 42
    | ^^^^^^^^^
  = help: Expected an object literal ('{...}') or array ('[...]') to construct 'User'. Got Int(42)
  = help: Use named-field construction with a map or positional construction with an array.
  = link: https://goblinlang.org/docs/errors#T0208

How to fix: Use either named-field or positional construction.

u|User = { name: "Alice" }      # map
# or
u|User = [ "Alice" ]            # array (positional)
u|User = { name: "Alice" }      # map
# or
u|User = [ "Alice" ]            # array (positional)