{"record":{"id":"afdc83935d024c59","repo":"JuliusBrussee/caveman","slug":"invalid-run-state-contract","errorCode":null,"errorMessage":"invalid run-state contract","messagePattern":"invalid run-state contract","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/runstate/runstate.go","lineNumber":140,"sourceCode":"\t}\n\tif err := tmp.Close(); err != nil {\n\t\treturn err\n\t}\n\treturn os.Rename(tmpName, Path(home, state.Port))\n}\n\nfunc read(home string, port int) (State, error) {\n\traw, err := os.ReadFile(Path(home, port))\n\tif err != nil {\n\t\treturn State{}, err\n\t}\n\tvar state State\n\tif err := json.Unmarshal(raw, &state); err != nil {\n\t\treturn State{}, err\n\t}\n\tif state.Schema != Schema || state.Port != port || state.PID < 1 ||\n\t\tstate.InstanceToken == \"\" || (state.Owner != \"wrap\" && state.Owner != \"start\") {\n\t\treturn State{}, errors.New(\"invalid run-state contract\")\n\t}\n\treturn state, nil\n}\n\ntype validators struct {\n\talive      func(int) bool\n\texecutable func(int) (string, error)\n\tbound      func(string) bool\n}\n\nfunc validate(state State, checks validators) bool {\n\tif !checks.alive(state.PID) {\n\t\treturn false\n\t}\n\texe, err := checks.executable(state.PID)\n\tif err != nil || !strings.Contains(strings.ToLower(filepath.Base(exe)), \"caveman-proxy\") {\n\t\treturn false\n\t}","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/runstate/runstate.go#L122-L158","documentation":"runstate.read loads the JSON state file for a given home/port and enforces its contract: schema must equal Schema, port must equal the file's own port, PID >= 1, instance token non-empty, and owner exactly \"wrap\" or \"start\". Any violation means the file is truncated, corrupt, forged, or from an incompatible version, and it is rejected outright.","triggerScenarios":"Reading a run-state file that was partially written (crash mid-write), hand-edited, copied from a different port, produced by a different schema version, or spoofed by another process dropping a file in the home directory.","commonSituations":"Process killed during state-file write leaving truncated JSON fields that parse but fail the contract; stale state from an older binary after upgrade; users editing the file to change the port or PID.","solutions":["Delete the invalid state file (it is derived data) and let the owner process rewrite it on next start","Ensure only one binary version writes to a given home during upgrades","If writing state yourself, emit all contract fields: schema, port, pid >= 1, instance token, owner of wrap|start"],"exampleFix":"// before\nstate, err := runstate.Read(home, port)\nif err != nil { return err }\n\n// after\nstate, err := runstate.Read(home, port)\nif err != nil {\n    if errors.Is(err, os.ErrNotExist) { /* not running */ return nil }\n    // invalid contract: treat as not-running and clear the corrupt file\n    _ = os.Remove(runstate.Path(home, port))\n    return nil\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"state, err := runstate.Read(home, port)\nif err != nil {\n    if errors.Is(err, os.ErrNotExist) {\n        // no instance running\n    } else {\n        // contract violation: treat as stale and remove the corrupt file\n        _ = os.Remove(runstate.Path(home, port))\n    }\n}","preventionTips":["Treat run-state files as disposable derived data; delete on contract violations","Write state atomically (temp file + rename) to avoid truncated writes","Never hand-edit run-state files"],"tags":["runstate","go","contract-validation","json","state-file"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}