unknwon/the-way-to-go_ZH_CN · error

%s:%d:%d: %v

Error message

%s:%d:%d: %v

What it means

Wrapping pattern from section 13.1: when JSON decoding fails with *json.SyntaxError, the caller type-asserts the error, converts serr.Offset (bytes read before the error) to line and column via a findLine helper, and returns fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err) — a file:line:col annotation like a compiler emits. This wrapper error is produced only for syntax-level malformed JSON.

Source

Thrown at eBook/13.1.md:118

作为第二个例子考虑用 `json` 包的情况。当 `json.Decode()` 在解析 JSON 文档发生语法错误时,指定返回一个 `SyntaxError` 类型的错误:

```go
type SyntaxError struct {
	msg    string // description of error
// error occurred after reading Offset bytes, from which line and columnnr can be obtained
	Offset int64
}

func (e *SyntaxError) Error() string { return e.msg }
```

在调用代码中你可以像这样用类型断言测试错误是不是上面的类型:

```go
if serr, ok := err.(*json.SyntaxError); ok {
	line, col := findLine(f, serr.Offset)
	return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
}
```

包也可以用额外的方法 (methods)定义特定的错误,比如 `net.Error`:

```go
package net
type Error interface {
	Timeout() bool   // Is the error a timeout?
	Temporary() bool // Is the error temporary?
}
```

在 [15.1 节](15.1.md) 我们可以看到怎么使用它。

正如你所看到的一样,所有的例子都遵循同一种命名规范:错误类型以 `...Error` 结尾,错误变量以 `err...` 或 `Err...` 开头或者直接叫 `err` 或 `Err`。

`syscall` 是低阶外部包,用来提供系统基本调用的原始接口。它们返回封装整数类型错误码的 `syscall.Errno`;类型 `syscall.Errno` 实现了 `Error` 接口。

View on GitHub (pinned to 7a54d34d36)

Solutions

  1. Open the reported file:line:col — the message localizes the first offending byte; fix the JSON there (comma, quote, brace)
  2. Implement findLine if you copy the pattern: scan the first serr.Offset bytes counting '\n' and the column as the remainder
  3. Validate before processing: dry-run decode, or jq/lint at ingest, so malformed input is rejected at the boundary
  4. If truncation is the cause, fix the writer (atomic temp+rename writes, verified content-length downloads) rather than patching files

Example fix

// before: bare error, no location
if err := dec.Decode(&v); err != nil {
	return err // "invalid character '}' looking for beginning of ..."
}

// after: locate the failure for the user
if serr, ok := err.(*json.SyntaxError); ok {
	line, col := findLine(f, serr.Offset)
	return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// dry-run decode at the trust boundary before business logic
if err := json.NewDecoder(bytes.NewReader(raw)).Decode(&v); err != nil {
	return err // reject malformed input on ingest
}

Type guard

func asSyntaxError(err error) (*json.SyntaxError, bool) {
	var serr *json.SyntaxError
	if errors.As(err, &serr) {
		return serr, true
	}
	return nil, false
}

Try / catch

if serr, ok := err.(*json.SyntaxError); ok {
	line, col := findLine(f, serr.Offset)
	return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
}
return err

Prevention

When it happens

Trigger: json.Decoder/Unmarshal on malformed JSON: trailing commas, single quotes, unescaped newlines inside strings, truncated streams. The branch fires specifically when err is *json.SyntaxError with a valid Offset; io.ErrUnexpectedEOF or *json.UnmarshalTypeError take other paths. Note findLine is not provided by encoding/json — you must implement it by counting newlines in the first Offset bytes.

Common situations: User- or machine-generated config/data files that get truncated (log rotation mid-write, partial downloads); hand-edited JSON with trailing commas; feeds switching to JSONL so extra objects after the first break strict decoding; readers surprised the message points at bytes, not the original line numbering of pretty-printed files.

Related errors


AI-assisted analysis of unknwon/the-way-to-go_ZH_CN@7a54d34d36 (2026-08-15). Data as JSON: /api/errors/f1e069718fb3b5e7. Report an issue: GitHub.