dagger/dagger · error
lockfile line %d: %w
Error message
lockfile line %d: %w
What it means
lockfile.Parse requires the first non-empty, non-comment line to be a valid version header (a JSON array like [["version","2"]]). If parseVersionHeader fails — invalid JSON, missing header, wrong shape, or an unsupported version — the error is wrapped with the 1-based line number.
Source
Thrown at util/lockfile/lockfile.go:80
// Lines starting with "#" are comments and are ignored. Non-empty files must
// otherwise start with a supported version header line.
func Parse(data []byte) (*Lockfile, error) {
lock := New()
lines := bytes.Split(data, []byte("\n"))
firstContentLine := true
version := ""
for i, rawLine := range lines {
line := strings.TrimSpace(string(rawLine))
if line == "" || strings.HasPrefix(line, commentPrefix) {
continue
}
if firstContentLine {
var err error
version, err = parseVersionHeader([]byte(line))
if err != nil {
return nil, fmt.Errorf("lockfile line %d: %w", i+1, err)
}
firstContentLine = false
continue
}
entry, err := parseEntry([]byte(line), version)
if err != nil {
return nil, fmt.Errorf("lockfile line %d: %w", i+1, err)
}
lock.entries[entryKey(entry.namespace, entry.operation, entry.inputsJSON)] = entry
}
return lock, nil
}
// Marshal encodes lockfile entries to deterministic JSON lines, preceded by
// HeaderComment and the version header. Comments present in the parsed input
// are not preserved.View on GitHub (pinned to 82ba2681db)
Solutions
- Restore the first content line to [["version","2"]] (run any dagger command to regenerate the lockfile)
- Delete the corrupted lockfile and let Dagger regenerate it (then re-run dagger update to re-pin)
- Check for git merge-conflict markers (<<<<<<<) near line 1 and resolve them
Example fix
// before (first content line)
["mymod","resolve",{},"sha256:..."]
// after
[["version","2"]]
["mymod","resolve",{},"sha256:..."] Defensive patterns
Strategy: validation
Validate before calling
lines := strings.Split(string(data), "\n")
for _, l := range lines {
l = strings.TrimSpace(l)
if l == "" || strings.HasPrefix(l, "#") {
continue
}
if l != `[["version","2"]]` {
return fmt.Errorf("first content line is not a v2 version header")
}
break
} Try / catch
lock, err := lockfile.Parse(data)
if err != nil && strings.HasPrefix(err.Error(), "lockfile line ") {
// regenerate instead of failing the pipeline
lock = lockfile.New()
} Prevention
- Never hand-edit the first content line of dagger.lock
- Resolve merge conflicts by regenerating the lockfile rather than merging by hand
- Commit lockfiles so corruption is visible in diffs and restorable from git
When it happens
Trigger: Calling Parse on bytes whose first content line is not [["version","2"]] — e.g. a random JSON entry, a v1 lockfile with a different header, truncated/corrupted first line, or a file starting with an entry instead of a header.
Common situations: Hand-editing dagger.lock and deleting/altering the header; merge conflicts that mangled the first line; old lockfile formats from earlier Dagger versions; files truncated by failed writes.
Related errors
- duplicate lock option %q
- invalid tuple length %d: expected 4 or 5 for lockfile versio
- invalid namespace: %w
- invalid operation: %w
- invalid inputs: %w
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/e7508cfdbb5c92c5.
Report an issue: GitHub.