diff --git a/README.md b/README.md
index 83437a0..f1b1484 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 In order to register sandbox executor, you need to run go-judge before running this program.
 
 ```bash
-$ make clean && make && ./_example/success/run.sh
+$ make clean && make && ./_example/success/run.sh && ./_example/compile_error/run.sh
 rm -rf ./build/*
 rm -rf *.out
 go build -o ./build/joj3 ./cmd/joj3
@@ -14,7 +14,15 @@ go build -o ./build/joj3 ./cmd/joj3
 + cd ./_example/success
 + ./../../build/joj3
 + cat ./joj3_result.json
-[{"Name":"compile","Results":[{"Score":100,"Comment":"compile done, executor status: run time: 265269232 ns, memory: 57790464 bytes"}]},{"Name":"run","Results":[{"Score":100,"Comment":"executor status: run time: 2033735 ns, memory: 13225984 bytes"},{"Score":100,"Comment":"executor status: run time: 3117399 ns, memory: 14548992 bytes"}]}]
+[{"Name":"compile","Results":[{"Score":0,"Comment":""}]},{"Name":"run","Results":[{"Score":100,"Comment":"executor status: run time: 2811900 ns, memory: 16658432 bytes"},{"Score":100,"Comment":"executor status: run time: 2578200 ns, memory: 13094912 bytes"}]}]
++ rm -f ./joj3_result.json
++ cd -
+++ dirname -- ./_example/compile_error/run.sh
++ DIRNAME=./_example/compile_error
++ cd ./_example/compile_error
++ ./../../build/joj3
++ cat ./joj3_result.json
+[{"Name":"compile","Results":[{"Score":0,"Comment":"Unexpected executor status: Nonzero Exit Status."}]}]
 + rm -f ./joj3_result.json
 + cd -
 ```
diff --git a/_example/success/conf.toml b/_example/success/conf.toml
index 2213fff..0d767da 100644
--- a/_example/success/conf.toml
+++ b/_example/success/conf.toml
@@ -1,4 +1,3 @@
-logLevel = 0
 [[stages]]
 name = "compile"
 [stages.executor]
diff --git a/cmd/joj3/conf.go b/cmd/joj3/conf.go
index 06cacfa..6441e7d 100644
--- a/cmd/joj3/conf.go
+++ b/cmd/joj3/conf.go
@@ -3,8 +3,8 @@ package main
 import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage"
 
 type Conf struct {
-	LogLevel   int
-	OutputPath string
+	LogLevel   int    `default:"0"`
+	OutputPath string `default:"joj3_result.json"`
 	Stages     []struct {
 		Name     string
 		Executor struct {
@@ -21,13 +21,6 @@ type Conf struct {
 	}
 }
 
-func DefaultConf() Conf {
-	return Conf{
-		LogLevel:   0,
-		OutputPath: "joj3_result.json",
-	}
-}
-
 type OptionalCmd struct {
 	Args   *[]string
 	Env    *[]string
diff --git a/cmd/joj3/main.go b/cmd/joj3/main.go
index 72b259a..167127e 100644
--- a/cmd/joj3/main.go
+++ b/cmd/joj3/main.go
@@ -10,19 +10,14 @@ import (
 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers"
 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage"
 
-	// "github.com/pelletier/go-toml/v2" may panic on some error
-	"github.com/BurntSushi/toml"
 	"github.com/jinzhu/copier"
+	"github.com/koding/multiconfig"
 )
 
-func parseConfFile(tomlPath *string) Conf {
-	tomlConfig, err := os.ReadFile(*tomlPath)
-	if err != nil {
-		slog.Error("read toml config", "error", err)
-		os.Exit(1)
-	}
-	conf := DefaultConf()
-	err = toml.Unmarshal(tomlConfig, &conf)
+func parseConfFile(path string) Conf {
+	m := multiconfig.NewWithPath(path)
+	conf := Conf{}
+	err := m.Load(&conf)
 	if err != nil {
 		slog.Error("parse stages config", "error", err)
 		os.Exit(1)
@@ -89,9 +84,9 @@ func outputResult(conf Conf, results []stage.StageResult) error {
 }
 
 func main() {
-	tomlPath := flag.String("c", "conf.toml", "file path of the toml config")
+	tomlPath := flag.String("c", "conf.toml", "file path of the config file")
 	flag.Parse()
-	conf := parseConfFile(tomlPath)
+	conf := parseConfFile(*tomlPath)
 	setupSlog(conf)
 	stages := generateStages(conf)
 	defer stage.Cleanup()
diff --git a/go.mod b/go.mod
index 54355ad..3aeba71 100644
--- a/go.mod
+++ b/go.mod
@@ -3,21 +3,25 @@ module focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3
 go 1.22.0
 
 require (
-	github.com/BurntSushi/toml v1.3.2
 	github.com/criyle/go-judge v1.8.2
 	github.com/mitchellh/mapstructure v1.5.0
 	google.golang.org/grpc v1.62.0
+	github.com/jinzhu/copier v0.4.0
+	github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7
 )
 
 require (
+	github.com/BurntSushi/toml v1.3.2 // indirect
 	github.com/creack/pty v1.1.21 // indirect
 	github.com/criyle/go-sandbox v0.10.1 // indirect
+	github.com/fatih/camelcase v1.0.0 // indirect
+	github.com/fatih/structs v1.1.0 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
-	github.com/jinzhu/copier v0.4.0
 	golang.org/x/net v0.21.0 // indirect
 	golang.org/x/sync v0.6.0 // indirect
 	golang.org/x/sys v0.17.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
 	google.golang.org/protobuf v1.32.0 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
 )
diff --git a/go.sum b/go.sum
index f7db9a6..5eaa021 100644
--- a/go.sum
+++ b/go.sum
@@ -6,6 +6,10 @@ github.com/criyle/go-judge v1.8.2 h1:dGjLqJRBifqsLVZj1scr23zdM4wPe98HTIVgYzPuRxA
 github.com/criyle/go-judge v1.8.2/go.mod h1:3RgsMp21D+UvXzkpOGsVFbLe2T2Lwk8jPEmCntQrvHQ=
 github.com/criyle/go-sandbox v0.10.1 h1:z9Il/UXQwKEvIwdr1wVheWWWAqGWtdTItBmEsWqFqT4=
 github.com/criyle/go-sandbox v0.10.1/go.mod h1:ivPw/HEh5unxVRlXJxCgkgTCuy+cxTkQDX7D2XQf/kg=
+github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
+github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
+github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
+github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
 github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
@@ -14,6 +18,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
 github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
+github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 h1:SWlt7BoQNASbhTUD0Oy5yysI2seJ7vWuGUp///OM4TM=
+github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7/go.mod h1:Y2SaZf2Rzd0pXkLVhLlCiAXFCLSXAIbTKDivVgff/AM=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
@@ -33,3 +39,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=