Skip to content

Cached PR title with leading newline can corrupt state.yml #5549

@sandsower

Description

@sandsower

Describe the bug

Lazygit can write a generated state.yml that it cannot read back on the next launch when a cached GitHub pull request title begins with a newline.

Startup then fails before the UI opens with an error like:

yaml: line 79: did not find expected key

The invalid state was in the generated GitHub PR cache under githubPullRequests:

githubPullRequests:
    /path/to/repo:
        - headRefName: example-branch
          number: 9
          title: |4-

            Example pull request title
          state: MERGED
          url: https://github.com/example/example/pull/9
          headRepositoryOwner: example

Removing ~/.local/state/lazygit/state.yml lets lazygit start again, but the problem can recur if the cache is written with the same title shape.

To Reproduce

This reproduces with the same gopkg.in/yaml.v3 marshal/unmarshal path used for app state:

package main

import (
    "fmt"

    "gopkg.in/yaml.v3"
)

type State struct {
    GithubPullRequests map[string][]PR `yaml:"githubPullRequests"`
}

type PR struct {
    HeadRefName string `yaml:"headRefName"`
    Number      int    `yaml:"number"`
    Title       string `yaml:"title"`
    State       string `yaml:"state"`
}

func main() {
    state := State{GithubPullRequests: map[string][]PR{
        "/tmp/repo": {{
            HeadRefName: "example-branch",
            Number:      9,
            Title:       "\nExample pull request title",
            State:       "MERGED",
        }},
    }}

    b, err := yaml.Marshal(state)
    fmt.Println(err)
    fmt.Print(string(b))

    var out State
    fmt.Println(yaml.Unmarshal(b, &out))
}

Output:

<nil>
githubPullRequests:
    /tmp/repo:
        - headRefName: example-branch
          number: 9
          title: |4-
            Example pull request title
          state: MERGED
yaml: line 2: did not find expected key

I also reproduced this against current master with a small regression test that:

  1. Creates an AppState with a CachedPullRequest.Title beginning with \n.
  2. Marshals it with yaml.Marshal.
  3. Unmarshals it back into AppState.

The unmarshal fails.

Expected behavior

Lazygit should not persist generated app state that prevents the next launch.

A possible fix is to normalize cached PR titles before app state serialization, or otherwise avoid the yaml.v3 leading-newline block scalar output for this field.

Version info

commit=v0.61.1, build date=2026-04-13T21:15:08Z, build source=binaryRelease, version=0.61.1, os=linux, arch=amd64, git version=2.54.0

I also checked current latest release, which is v0.61.1.

Terminal info

Linux shell/tmux environment.

This does not appear to be terminal-rendering related; startup fails while parsing state.yml.

Additional context

The path involved appears to be:

  • pkg/gui/controllers/helpers/refresh_helper.go copies pr.Title into config.CachedPullRequest.Title
  • pkg/config/app_config.go writes AppState using yaml.Marshal(c.appState)
  • the next launch reads that file using yaml.Unmarshal(appStateBytes, appState)

I tested one small local patch that made the regression test pass:

func (c CachedPullRequest) MarshalYAML() (interface{}, error) {
    type cachedPullRequest CachedPullRequest
    result := cachedPullRequest(c)
    result.Title = strings.TrimLeft(result.Title, "\r\n")
    return result, nil
}

That may not be the preferred upstream fix, but it prevents this specific cache field from making state.yml unreadable.

I saw the current contributing note about opening an issue before proposing a PR. Happy to provide the regression test/patch if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions