{"record":{"id":"0a37d368846b0e19","repo":"hyperledger/fabric","slug":"failed-to-restore-working-directory-s","errorCode":null,"errorMessage":"failed to restore working directory: %s","messagePattern":"failed to restore working directory: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/chaincode/platforms/golang/platform.go","lineNumber":389,"sourceCode":"\t\treturn false, err\n\tdefault:\n\t\treturn fi.Mode().IsRegular(), nil\n\t}\n}\n\nfunc moduleInfo(path string) (*ModuleInfo, error) {\n\tentryWD, err := os.Getwd()\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to get working directory\")\n\t}\n\n\t// directory doesn't exist so unlikely to be a module\n\tif err := os.Chdir(path); err != nil {\n\t\treturn nil, nil\n\t}\n\tdefer func() {\n\t\tif err := os.Chdir(entryWD); err != nil {\n\t\t\tpanic(fmt.Sprintf(\"failed to restore working directory: %s\", err))\n\t\t}\n\t}()\n\n\t// Using `go list -m -f '{{ if .Main }}{{.GoMod}}{{ end }}' all` may try to\n\t// generate a go.mod when a vendor tool is in use. To avoid that behavior\n\t// we use `go env GOMOD` followed by an existence check.\n\tcmd := exec.Command(\"go\", \"env\", \"GOMOD\")\n\tcmd.Env = os.Environ()\n\toutput, err := cmd.Output()\n\tif err != nil {\n\t\treturn nil, wrapExitErr(err, \"failed to determine module root\")\n\t}\n\n\tmodExists, err := regularFileExists(strings.TrimSpace(string(output)))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !modExists {","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/chaincode/platforms/golang/platform.go#L371-L407","documentation":"moduleInfo chdirs into the chaincode path and defers a restore of the original working directory; if the restore chdir fails, it panics with 'failed to restore working directory: %s'. This is a hard safety failure to avoid leaving the process in the wrong directory.","triggerScenarios":"The entry working directory captured by os.Getwd is deleted or becomes inaccessible while moduleInfo is executing inside the chaincode path, so the deferred os.Chdir(entryWD) fails and panics.","commonSituations":"Concurrent cleanup deleting the peer's original cwd, containers churning tmpfs mounts, race between packaging and tmp dir removal.","solutions":["Ensure the peer's original working directory remains mounted and exists during chaincode packaging.","Avoid deleting temp/mount directories concurrently with packaging operations.","Run the peer from a stable directory (e.g. /etc/hyperledger/fabric) rather than ephemeral paths."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"wd, err := os.Getwd()\nif err != nil { return err }\nif _, err := os.Stat(wd); err != nil { return fmt.Errorf(\"cwd will be unchdir-able: %w\", err) }","typeGuard":null,"tryCatchPattern":"// moduleInfo panics on restore failure; guard at a higher level\nfunc safeDescribe(path string) (out *golang.CodeDescriptor, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"moduleInfo panic: %v\", r)\n\t\t}\n\t}()\n\treturn golang.DescribeCode(path)\n}","preventionTips":["Run the peer from a stable directory like /etc/hyperledger/fabric","Avoid concurrent tmp-dir cleanup during packaging","Keep mounts serving the peer's cwd alive for the process lifetime"],"tags":["panic","working-directory","filesystem","hyperledger-fabric"],"backgroundTag":"working-directory-restore-panic","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}