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

  1. Restore the first content line to [["version","2"]] (run any dagger command to regenerate the lockfile)
  2. Delete the corrupted lockfile and let Dagger regenerate it (then re-run dagger update to re-pin)
  3. 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

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


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/e7508cfdbb5c92c5. Report an issue: GitHub.