# \`get()\` returns default value instead of value matching to valid key when default value is an \`error()\`

**URL:** https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670
**Category:** New to Julia
**Tags:** error, dictionary, get
**Created:** [June 17, 2026, 2:08pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670 "2026-06-17T14:08:38Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![jlawrie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlawrie/32/26283_2.png) [@jlawrie](https://discourse.julialang.org/u/jlawrie)
#### Post date: [June 17, 2026, 2:08pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/1 "2026-06-17T14:08:38Z")

</div>

Hello everyone!

Looking at the MWE below:

It appears `error`/`throw` are run before `get` is finished, causing `get` to be interrupted and the default value to be returned, rather than the `value` which matches the valid `key` as expected.

**Is this the intended behaviour for `get` in this circumstance?**

I think it is rarely necessary to define an `error()` as the default value for `get()` - I stumbled upon this purely by accident this afternoon.

To my understanding there are (several) other ways to include this function that avoid this particular behaviour.

I wanted to post this here in-case anyone else comes across the same thing in the future and wonders (as I did) why passing a valid `key` to their `Dict()` did not returning the matching stored `value`, rather the `error()` set as the default value.

Thanks and have a nice day!

* * *

**MWE**

```julia

d = Dict("a" => 1, "b" =>2)
k = "a"
err_msg = "Key '$k' not present in dictionary: $d"
@assert haskey(d, k) err_msg

# While k = "a", the assertions hold, else 'err_msg' is returned
@assert get(d, k, 0) == 1 err_msg
@assert get(d, k, "Default value") == 1 err_msg
@assert get(d, k, ArgumentError("No matching key found")) == 1 err_msg

# While k = "a", the error including the string "Default value" is returned, before the assertion test is completed
@assert get(d, k, error("Default value")) == 1 err_msg
@assert get(d, k, throw(ArgumentError("Default value"))) == 1 err_msg

```

* * *

**Related discussion** (discovered automatically by discourse)

- [List item](https://discourse.julialang.org/t/why-get-d-k-default-allows-default-of-a-type-different-than-valtype-d/12139)

* * *

**Output from `versioninfo()`** (incase relevant)

```julia
julia> versioninfo()
Julia Version 1.12.6
Commit 15346901f00 (2026-04-09 19:20 UTC)
Build Info:
  Official https://julialang.org release
Platform Info:
  OS: macOS (arm64-apple-darwin24.0.0)
  CPU: 8 × Apple M1
  WORD_SIZE: 64
  LLVM: libLLVM-18.1.7 (ORCJIT, apple-m1)
  GC: Built with stock GC
Threads: 1 default, 1 interactive, 1 GC (on 4 virtual cores)
Environment:
  JULIA_EDITOR = code
  JULIA_VSCODE_REPL = 1

```

* * *

---

<div class="post-metadata">

### Author: ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)
#### Post date: [June 17, 2026, 2:31pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/2 "2026-06-17T14:31:56Z")

</div>

yes, this is intended behavior and has nothing to do with `get`

`ArgumentError` is an object like anything else, something like (paraphrasing)

```julia-auto
struct ArgumentError
    msg::String
end

```

so you can use it as a value, pass it around, assign to variables, etc.

but `throw` and `error` are not values. they are routines with side effects where the side effect is to raise some kind of error. see the difference:

```julia-auto
julia> struct MyError x end

julia> MyError(123)
MyError(123)

julia> throw(MyError(123))
ERROR: MyError(123)

```

so `throw` is basically the primitive that does the error-throwing part of any object you pass it. and then `error` is more like a convenience function. you can think of `error` as (again, paraphrasing) something like `error(msg) = throw(ErrorException(msg))`

the reason you’re seeing this in the `get` call is that all function arguments are evaluated before running the function body. so the value you pass into `default` for `get` is _not_ lazily evaluated.

you can see similar behavior like this

```julia-auto
julia> get(Dict('a'=>1), 'a', println("hello, world!"))
hello, world!
1

julia> get(Dict('a'=>1), 'b', println("hello, world!"))
hello, world!

```

where the default value is in fact `nothing`, since this is what `println` actually evaluates to, but we still have the side effect (in this case printing to stdout).

to salvage the behavior you want, there is another `get` signature

```julia-auto
get(f::Union{Function, Type}, collection, key)

  Return the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.

```

where you could do

```julia-auto
julia> get(Dict('a'=>1), 'b') do
           error("not found")
       end
ERROR: not found
Stacktrace:
 [1] error(s::String)
   @ Base error.jl:56
 [2] (::var"#5#6")()
   @ Main REPL[12]:2
 [3] get(default::var"#5#6", h::Dict{Char, Int64}, key::Char)
   @ Base dict.jl:528
 [4] top-level scope
   @ REPL[12]:1

```

---

<div class="post-metadata">

### Author: ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)
#### Post date: [June 17, 2026, 2:55pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/3 "2026-06-17T14:55:18Z")

</div>

Seems that `get` is not lazy, it evaluates the expression given as default even if the key is found:

```julia-auto
julia> d = Dict("a" => 1, "b" =>2)
Dict{String, Int64} with 2 entries:
  "b" => 2
  "a" => 1
julia> obj = [1]
1-element Vector{Int64}:
 1
julia> get(d, "a", begin obj[1] = 10 end)
1
julia> obj[1]
10

```

The version with a function as first element is instead lazy:

```julia-auto
julia> obj[1] = 1
1
julia> get(d, "a") do
           obj[1] = 10
       end
1
julia> obj[1]
1
julia> get(d, "z") do
           obj[1] = 10
       end
10
julia> obj[1]
10

```

---

<div class="post-metadata">

### Author: ![jlawrie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlawrie/32/26283_2.png) [@jlawrie](https://discourse.julialang.org/u/jlawrie)
#### Post date: [June 17, 2026, 3:13pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/4 "2026-06-17T15:13:28Z")

</div>

Thank you for the detailed explanations for why the observed behaviour happens and pointing out the alternative syntax `get(d, k) do f()` which causes the defined ‘default value’ to be evaluated after the attempt to find the `key` in the `dict` is complete!

---

<div class="post-metadata">

### Author: ![jlawrie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlawrie/32/26283_2.png) [@jlawrie](https://discourse.julialang.org/u/jlawrie)
#### Post date: [June 17, 2026, 3:18pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/5 "2026-06-17T15:18:13Z")

</div>

Thank you as well for showing the difference between the two syntaxes for `get()`!

I’m now much more likely to check whether _lazy_ evaluation is used for a function or not, or, as in this case, if the user can choose between two implementations for _laziness_ if they wish.

---

<div class="post-metadata">

### Author: ![BioTurboNick](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bioturbonick/32/6380_2.png) [@BioTurboNick](https://discourse.julialang.org/u/BioTurboNick)
#### Post date: [June 17, 2026, 3:28pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/6 "2026-06-17T15:28:12Z")

</div>

This isn’t related to `get` not being lazy. In Julia, `begin...end` is just a code block, and as such is executed as soon as it is reached.

---

<div class="post-metadata">

### Author: ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)
#### Post date: [June 17, 2026, 3:34pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/7 "2026-06-17T15:34:05Z")

</div>

yes, but it seems that in the first version `get(iterator,key,default)` whatever is “default” is reached before the evaluation of checking for the key, while with the `get(f,iterator,key)` this is done before..

---

<div class="post-metadata">

### Author: ![BioTurboNick](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bioturbonick/32/6380_2.png) [@BioTurboNick](https://discourse.julialang.org/u/BioTurboNick)
#### Post date: [June 17, 2026, 3:38pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/8 "2026-06-17T15:38:30Z")

</div>

That’s just how Julia parses and executes anything. Whatever you put as an argument is evaluated fully before the outer function is called with that argument.

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [June 19, 2026, 10:39am UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/9 "2026-06-19T10:39:48Z")

</div>

> [@sylvaticus](#):
>
> yes, but it seems that in the first version `get(iterator,key,default)` whatever is “default” is reached before the evaluation of checking for the key,

This is not specific to `get`, it’s just how function calls work: all of the arguments are evaluated and _then_ passed to the function.

It’s not even specific to Julia: _almost all_ programming languages execute function calls like this.

(The exception being special syntaxes like `&&` that look like function/operator calls but are actually control flow.)

---

<div class="post-metadata">

### Author: ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)
#### Post date: [June 19, 2026, 11:14am UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/10 "2026-06-19T11:14:06Z")

</div>

> [@stevengj](#):
>
> It’s not even specific to Julia: _almost all_ programming languages execute function calls like this.

I think it’s instructive to look at languages where this is different. Take scala:

```julia-auto
> def lazyCall(cond:Boolean, someBlock: => Any):Any = if(cond) someBlock else null
def lazyCall(cond: Boolean, someBlock: => Any): Any

> def eagerCall(cond:Boolean, item:Any):Any = if(cond) item else null
def eagerCall(cond: Boolean, item: Any): Any

> lazyCall(false, throw new RuntimeException("oops"))
val res0: Any = null

> eagerCall(false, throw new RuntimeException("oops"))
java.lang.RuntimeException: oops
  ... 39 elided

```

For scala, it depends on the callee declaration whether it accepts a zero-argument anonymous function (“block” argument), which necessarily creates a closure for lazy evaluation, or an eagerly evaluated value.

It is impossible to guess from reading the callsite code.

This is similar in spirit to C++: Functions can take “reference” arguments (which really are pointers), and it is impossible to guess from reading the callsite code whether you’re passing a pointer or a value.

===========

This syntax is very pretty and convenient. But I think it is terrible design. I hate it. Code is being read more than it is written. Be explicit.

Thank you julia for not doing something terrible like that.

Luckily there is no risk of well-meaning people trying to introduce something like that to julia: This kind of bullshit only works if the callsite statically knows the callee declaration; and julia is a dynamic, not static language.

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [June 19, 2026, 12:26pm UTC](https://discourse.julialang.org/t/get-returns-default-value-instead-of-value-matching-to-valid-key-when-default-value-is-an-error/137670/11 "2026-06-19T12:26:09Z")

</div>

> [@foobar\_lv2](#):
>
> Luckily there is no risk of well-meaning people trying to introduce something like that to julia: This kind of bullshit only works if the callsite statically knows the callee declaration; and julia is a dynamic, not static language.

There are dynamic languages with lazy evaluation, like R, but then every argument is always lazily evaluated. Once, at the first use in the called function, in the callers environment.
