bug: runner gets into a defunct state if reporting job result to the Forgejo instance times out #1166

Closed
opened 2025-11-20 05:00:25 +00:00 by alexrp · 16 comments
Contributor

Can you reproduce the bug on the Forgejo test instance?

No

Description

On the same slow machine that #1091 is happening on, we see cases where reporting the job result fails like this:

... snip ...
Nov 20 04:30:44 callisto forgejo-runner[164637]: [ci/riscv64-linux-debug]   ✅  Success - Post Checkout
Nov 20 04:30:44 callisto forgejo-runner[164637]: [ci/riscv64-linux-debug] 🏁  Job succeeded
Nov 20 04:30:44 callisto forgejo-runner[164637]: time="2025-11-20T04:30:44+01:00" level=debug msg="Cleaning up container for job FORGEJO-ACTIONS-TASK-2245566_WORKFLOW-3187898fe12a820e657871ac208125b8b7420c7a7ada95851e9b1067b4380357_JOB-riscv64-linux-debug" func="[func6]" file="[job_executor.go:127]"
Nov 20 04:30:44 callisto forgejo-runner[164637]: time="2025-11-20T04:30:44+01:00" level=debug msg=stopHostEnvironment func="[stopHostEnvironment]" file="[run_context.go:257]"
Nov 20 05:47:31 callisto forgejo-runner[164637]: time="2025-11-20T05:47:31+01:00" level=warning msg="ReportLog error: unavailable: write tcp [2a13:8a02:5008:fb00:fcfe:feff:fe48:19e9]:49254->[2a0a:4580:103f:c0de::1]:443: write: connection timed out" func="[RunDaemon]" file="[reporter.go:203]"

There are no further logs past this point; after this, the runner daemon keeps going but appears as offline on Forgejo and no longer takes new jobs. This then requires manual intervention to fix.

I'd say there are 2 things that need to be done here:

  1. The runner should try harder to report job results to the Forgejo instance. After an 8-hour long job, it's really unfortunate to end up with a failure that then requires another 8-hour long retry to correct.
  2. The runner should not become effectively defunct (while appearing working to systemd) when a failure like this does happen.

Forgejo Version

Codeberg current

Runner Version

v11.3.1

How are you running Forgejo?

Codeberg

How are you running the Runner?

make build + systemd

Logs

No response

Workflow file

No response

### Can you reproduce the bug on the Forgejo test instance? No ### Description On the same slow machine that #1091 is happening on, we see cases where reporting the job result fails like this: ``` ... snip ... Nov 20 04:30:44 callisto forgejo-runner[164637]: [ci/riscv64-linux-debug] ✅ Success - Post Checkout Nov 20 04:30:44 callisto forgejo-runner[164637]: [ci/riscv64-linux-debug] 🏁 Job succeeded Nov 20 04:30:44 callisto forgejo-runner[164637]: time="2025-11-20T04:30:44+01:00" level=debug msg="Cleaning up container for job FORGEJO-ACTIONS-TASK-2245566_WORKFLOW-3187898fe12a820e657871ac208125b8b7420c7a7ada95851e9b1067b4380357_JOB-riscv64-linux-debug" func="[func6]" file="[job_executor.go:127]" Nov 20 04:30:44 callisto forgejo-runner[164637]: time="2025-11-20T04:30:44+01:00" level=debug msg=stopHostEnvironment func="[stopHostEnvironment]" file="[run_context.go:257]" Nov 20 05:47:31 callisto forgejo-runner[164637]: time="2025-11-20T05:47:31+01:00" level=warning msg="ReportLog error: unavailable: write tcp [2a13:8a02:5008:fb00:fcfe:feff:fe48:19e9]:49254->[2a0a:4580:103f:c0de::1]:443: write: connection timed out" func="[RunDaemon]" file="[reporter.go:203]" ``` There are no further logs past this point; after this, the runner daemon keeps going but appears as offline on Forgejo and no longer takes new jobs. This then requires manual intervention to fix. I'd say there are 2 things that need to be done here: 1. The runner should try harder to report job results to the Forgejo instance. After an 8-hour long job, it's *really* unfortunate to end up with a failure that then requires another 8-hour long retry to correct. 2. The runner should not become effectively defunct (while appearing working to systemd) when a failure like this does happen. ### Forgejo Version Codeberg current ### Runner Version v11.3.1 ### How are you running Forgejo? Codeberg ### How are you running the Runner? `make build` + systemd ### Logs _No response_ ### Workflow file _No response_
Owner

The "ReportLog error: ..." message comes from RunDaemon, which is the background process that spools logs to Forgejo. A warning in this process is just a warning printed to the logs; it continues to send logs at reportInterval (1s) in spite of the warning:

err := r.ReportLog(false)
if err != nil {
log.Warnf("ReportLog error: %v", err)
}

When a job completes, the final logs & state are transmitted to Forgejo in a retry loop; theoretically this should attempt 10 retries with 100ms delays between. Although this isn't the relevant code path observed in the logs, just noting that it does exist when finalizing a job:

return retry.Do(func() error {
if err := r.ReportLog(true); err != nil {
return err
}
return r.ReportState()
}, retry.Context(r.ctx))

Another thought reviewing this code is that the error condition might leave an internal lock being held, preventing future progress. I can't see any obvious case of that that -- when ReportLog is executed and returns an error at line 358 (where it could get "connection timed out"), it looks like all the internal locks would be released and so nothing should prevent it from continuing to operate.

func (r *Reporter) ReportLog(noMore bool) error {
r.clientM.Lock()
defer r.clientM.Unlock()
r.stateMu.RLock()
rows := r.logRows
r.stateMu.RUnlock()
if needMore := r.masker.replace(rows, noMore); needMore {
return NewErrRetry(errRetryNeedMoreRows)
}
resp, err := r.client.UpdateLog(r.ctx, connect.NewRequest(&runnerv1.UpdateLogRequest{
TaskId: r.state.Id,
Index: int64(r.logOffset),
Rows: rows,
NoMore: noMore,
}))
if err != nil {
return err
}
ack := int(resp.Msg.GetAckIndex())
if ack < r.logOffset {
return fmt.Errorf("submitted logs are lost %d < %d", ack, r.logOffset)
}
r.stateMu.Lock()
r.logRows = r.logRows[ack-r.logOffset:]
r.logOffset = ack
r.stateMu.Unlock()
if noMore && len(r.logRows) > 0 {
return NewErrRetry(errRetrySendAll, len(r.logRows))
}
return nil
}

I don't see a clear problem with any of this, but here's some plausible things I'll check into.

  • Maybe all the retry code is in place, but it all attempts share a network client that could be broken and stateful preventing any recovery
  • The defaults for the Close retry may not really be the documented 10 retry attempts -- it could fall into an infinite retry loop and not reporting/logging the error

I'll post an update when I can reproduce some similar errors and see what really happens.

The "ReportLog error: ..." message comes from `RunDaemon`, which is the background process that spools logs to Forgejo. A warning in this process is just a warning printed to the logs; it continues to send logs at `reportInterval` (1s) in spite of the warning: https://code.forgejo.org/forgejo/runner/src/commit/478b11bd08ecad96e086b14e1137f18120fe2cfa/internal/pkg/report/reporter.go#L201-L204 When a job completes, the final logs & state are transmitted to Forgejo in a retry loop; theoretically this should attempt 10 retries with 100ms delays between. Although this isn't the relevant code path observed in the logs, just noting that it does exist when finalizing a job: https://code.forgejo.org/forgejo/runner/src/commit/478b11bd08ecad96e086b14e1137f18120fe2cfa/internal/pkg/report/reporter.go#L309-L314 Another thought reviewing this code is that the error condition might leave an internal lock being held, preventing future progress. I can't see any obvious case of that that -- when `ReportLog` is executed and returns an error at line 358 (where it could get "connection timed out"), it looks like all the internal locks would be released and so nothing should prevent it from continuing to operate. https://code.forgejo.org/forgejo/runner/src/commit/478b11bd08ecad96e086b14e1137f18120fe2cfa/internal/pkg/report/reporter.go#L339-L376 I don't see a clear problem with any of this, but here's some plausible things I'll check into. - Maybe all the retry code is in place, but it all attempts share a network client that could be broken and stateful preventing any recovery - The defaults for the `Close` retry may not really be the documented 10 retry attempts -- it could fall into an infinite retry loop **and** not reporting/logging the error I'll post an update when I can reproduce some similar errors and see what really happens.
Owner

I've tested a couple scenarios and have found that:

  • The network client seems safe to reuse with failures to contact Forgejo, without any tested or documented indication that problems can become persistent in the client.
  • The retry code works well with retry attempts 10 times over the next 50 seconds when closing the reporter.
  • When retry fails persistently, the runner is frozen while retrying but then becomes available for new jobs.
  • Errors encountered in the retry process are not logged.

The most obvious fix here is to ensure that retry attempt errors are logged for clarity. @alexrp: 10 retries over 50 seconds (+ time to execute) is the intended value, do you think that is a reasonable configuration? It could be tweaked to more retries or more delays between them.

The HTTP client used for connection to Forgejo relies on default values for timeouts. The scenario I can imagine is that this box had some network issue, and that the client was either:

  • busy doing retries with nothing being logged, and would eventually give up and try to fetch a new job (possibly stopped by the network problem),
  • attempting to reconnect and stuck in a timeout that isn't defined in the default HTTP client, such as the TLSHandshakeTimeout which has a default of "no timeout".

So I think doing a pass on the timeouts and ensuring there are none that are undefined is also warranted. It's my best guess as to why the runner would have been frozen, assuming it was like that for a long time when you reached it.

TODO:

  • Clearer retry logging
  • Ensure all HTTP timeouts are defined and reasonable
  • (Maybe tweak retry settings)
I've tested a couple scenarios and have found that: - The network client seems safe to reuse with failures to contact Forgejo, without any tested or documented indication that problems can become persistent in the client. - The retry code works well with retry attempts 10 times over the next 50 seconds when closing the reporter. - When retry fails persistently, the runner is frozen while retrying but then becomes available for new jobs. - Errors encountered in the retry process are not logged. The most obvious fix here is to ensure that retry attempt errors are logged for clarity. @alexrp: 10 retries over 50 seconds (+ time to execute) is the intended value, do you think that is a reasonable configuration? It could be tweaked to more retries or more delays between them. The HTTP client used for connection to Forgejo relies on default values for timeouts. The scenario I can imagine is that this box had some network issue, and that the client was either: - busy doing retries with nothing being logged, and would eventually give up and try to fetch a new job (possibly stopped by the network problem), - attempting to reconnect and stuck in a timeout that isn't defined in the default HTTP client, such as the `TLSHandshakeTimeout` which has a default of "no timeout". So I think doing a pass on the timeouts and ensuring there are none that are undefined is also warranted. It's my best guess as to why the runner would have been frozen, assuming it was like that for a long time when you reached it. TODO: - Clearer retry logging - Ensure all HTTP timeouts are defined and reasonable - (Maybe tweak retry settings)
Author
Contributor

For what it's worth, I don't think we're looking at some intermittent network issue; almost every job that gets scheduled on this machine over the last few days runs into network timeout problems, with only a couple exceptions.

This machine is just very slow. Even something as simple as printing systemd logs takes a long time -- multiple minutes -- compared to the other machines with equivalent hardware. I think we just lost the silicon lottery with this particular machine.

@mfenniak wrote in #1166 (comment):

The HTTP client used for connection to Forgejo relies on default values for timeouts.

I imagine this is the problem? I think the timeout itself just needs to be longer. It would be nice if all network-related timeouts were configurable in config.yaml so we could mitigate this machine's slowness that way.

For what it's worth, I don't think we're looking at some intermittent network issue; almost every job that gets scheduled on this machine over the last few days runs into network timeout problems, with only a couple exceptions. This machine is just very slow. Even something as simple as printing systemd logs takes a long time -- multiple minutes -- compared to the other machines with equivalent hardware. I think we just lost the silicon lottery with this particular machine. @mfenniak wrote in https://code.forgejo.org/forgejo/runner/issues/1166#issuecomment-67396: > The HTTP client used for connection to Forgejo relies on default values for timeouts. I imagine this is the problem? I think the timeout itself just needs to be longer. It would be nice if all network-related timeouts were configurable in `config.yaml` so we could mitigate this machine's slowness that way.
Owner

@alexrp wrote in #1166 (comment):

I imagine this is the problem? I think the timeout itself just needs to be longer. It would be nice if all network-related timeouts were configurable in config.yaml so we could mitigate this machine's slowness that way.

The default HTTP client being used has no timeout, making some operations infinite. Typically this has some other bound, like the max job length -- but when performing the final log reporting, it escapes that bound so that even a job that hits the job timeout can report its cancellation. This could explain the system becoming frozen; if it has capacity 1 and hits this infinite timeout, it could become stuck. It also won't effectively retry if it's stuck in an operation with no timeout.

(It isn't clear to me what specific operation/failure could really be infinite in this configuration... most TCP interactions have some kind of exit mode assuming a well behaved server counterpart.)

#1171 proposes to fix this with making HTTP interactions to Forgejo have a timeout. A one minute timeout is the default, and it can be overridden by the environment variable FORGEJO_CLIENT_TIMEOUT (in seconds).

@alexrp wrote in https://code.forgejo.org/forgejo/runner/issues/1166#issuecomment-67471: > I imagine this is the problem? I think the timeout itself just needs to be longer. It would be nice if all network-related timeouts were configurable in `config.yaml` so we could mitigate this machine's slowness that way. The default HTTP client being used has no timeout, making some operations infinite. Typically this has some other bound, like the max job length -- but when performing the final log reporting, it escapes that bound so that even a job that hits the job timeout can report its cancellation. This could explain the system becoming frozen; if it has capacity 1 and hits this infinite timeout, it could become stuck. It also won't *effectively* retry if it's stuck in an operation with no timeout. (It isn't clear to me what specific operation/failure could really be infinite in this configuration... most TCP interactions have some kind of exit mode assuming a well behaved server counterpart.) #1171 proposes to fix this with making HTTP interactions to Forgejo have a timeout. A one minute timeout is the default, and it can be overridden by the environment variable `FORGEJO_CLIENT_TIMEOUT` (in seconds).
Author
Contributor

Ok, I see. It seems like the only practical path forward here is to get #1170 and #1171 actually deployed on the machine, see what happens, and proceed from there.

Ok, I see. It seems like the only practical path forward here is to get #1170 and #1171 actually deployed on the machine, see what happens, and proceed from there.
Owner

@mfenniak i think the retry should use an increasing delay so the retry can better handle longer remote failures

The retry should never be infinity by default without setting Attempts(0) 🤔

@mfenniak i think the retry should use an increasing delay so the retry can better handle longer remote failures The retry should never be infinity by default without setting `Attempts(0)` 🤔 - https://github.com/avast/retry-go?tab=readme-ov-file#func--backoffdelay
Author
Contributor

Interestingly, the machine in question just reconnected to Codeberg after going offline (due to this issue) some hours earlier, so it seems it doesn't truly get stuck.

It's currently running a job so I can't really do much on it, but once it's done, I'll see if there's anything interesting in the service log.

Interestingly, the machine in question just reconnected to Codeberg after going offline (due to this issue) some hours earlier, so it seems it doesn't truly get stuck. It's currently running a job so I can't really do much on it, but once it's done, I'll see if there's anything interesting in the service log.
Owner

@viceice wrote in #1166 (comment):

@mfenniak i think the retry should use an increasing delay so the retry can better handle longer remote failures

It does -- the default is 100ms with doubling between retries. I think maybe more time between retries makes sense in this very slow machine... but it's hard to say? So I've put in some new config options to allow some flexibility: #1173

@viceice wrote in https://code.forgejo.org/forgejo/runner/issues/1166#issuecomment-67498: > @mfenniak i think the retry should use an increasing delay so the retry can better handle longer remote failures It does -- the default is 100ms with doubling between retries. I think *maybe* more time between retries makes sense in this very slow machine... but it's hard to say? So I've put in some new config options to allow some flexibility: https://code.forgejo.org/forgejo/runner/pulls/1173
Owner

Forgejo Runner v12.0.0 has been released with these hopefully relevant fixes:

  • retry logging, indicating if there's ongoing errors #1170
  • fix missing http timeout #1171
  • retry configuration options #1172

I'm going to tentatively close this issue, but if there's ongoing problems and more we can do here please feel free to reopen it with additional information.

Forgejo Runner v12.0.0 has been released with these hopefully relevant fixes: - retry logging, indicating if there's ongoing errors #1170 - fix missing http timeout #1171 - retry configuration options #1172 I'm going to tentatively close this issue, but if there's ongoing problems and more we can do here please feel free to reopen it with additional information.
Author
Contributor

Okay, after upgrading to v12.0.0 (and not changing anything in config.yaml), we got this:

Nov 22 18:33:29 callisto forgejo-runner[69307]: [ci/riscv64-linux-release]   ✅  Success - Post Checkout
Nov 22 18:33:29 callisto forgejo-runner[69307]: [ci/riscv64-linux-release] 🏁  Job succeeded
Nov 22 18:33:29 callisto forgejo-runner[69307]: time="2025-11-22T18:33:29+01:00" level=debug msg="Cleaning up container for job FORGEJO-ACTIONS-TASK-2268600_WORKFLOW-d2cdbcbb773043295eb4a31cea438a95978d93fceac4654761015a8a98aaaa20_JOB-riscv64-linux-release" func="[func7]" file="[job_executor.go:142]"
Nov 22 18:33:29 callisto forgejo-runner[69307]: time="2025-11-22T18:33:29+01:00" level=debug msg=stopHostEnvironment func="[stopHostEnvironment]" file="[run_context.go:257]"
Nov 22 18:33:39 callisto forgejo-runner[69307]: retry = &config.Retry{MaxRetries:0xa, InitialDelay:100000000, MaxDelay:0}
Nov 22 18:34:26 callisto forgejo-runner[69307]: time="2025-11-22T18:34:26+01:00" level=warning msg="ReportLog error: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[RunDaemon]" file="[reporter.go:206]"
Nov 22 18:35:26 callisto forgejo-runner[69307]: time="2025-11-22T18:35:26+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:36:26 callisto forgejo-runner[69307]: time="2025-11-22T18:36:26+01:00" level=warning msg="ReportState error: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateTask\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[RunDaemon]" file="[reporter.go:210]"
Nov 22 18:37:26 callisto forgejo-runner[69307]: time="2025-11-22T18:37:26+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:38:26 callisto forgejo-runner[69307]: time="2025-11-22T18:38:26+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:39:27 callisto forgejo-runner[69307]: time="2025-11-22T18:39:27+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:40:28 callisto forgejo-runner[69307]: time="2025-11-22T18:40:28+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:41:29 callisto forgejo-runner[69307]: time="2025-11-22T18:41:29+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:42:33 callisto forgejo-runner[69307]: time="2025-11-22T18:42:33+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:43:39 callisto forgejo-runner[69307]: time="2025-11-22T18:43:39+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:44:52 callisto forgejo-runner[69307]: time="2025-11-22T18:44:52+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:46:18 callisto forgejo-runner[69307]: time="2025-11-22T18:46:18+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]"
Nov 22 18:46:18 callisto forgejo-runner[69307]: time="2025-11-22T18:46:18+01:00" level=error msg="unable to send final job logs and status: All attempts fail:\n#1: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#2: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#3: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#4: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#5: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#6: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#7: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#8: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#9: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#10: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func1]" file="[runner.go:174]"

Okay, after upgrading to v12.0.0 (and not changing anything in `config.yaml`), we got this: ``` Nov 22 18:33:29 callisto forgejo-runner[69307]: [ci/riscv64-linux-release] ✅ Success - Post Checkout Nov 22 18:33:29 callisto forgejo-runner[69307]: [ci/riscv64-linux-release] 🏁 Job succeeded Nov 22 18:33:29 callisto forgejo-runner[69307]: time="2025-11-22T18:33:29+01:00" level=debug msg="Cleaning up container for job FORGEJO-ACTIONS-TASK-2268600_WORKFLOW-d2cdbcbb773043295eb4a31cea438a95978d93fceac4654761015a8a98aaaa20_JOB-riscv64-linux-release" func="[func7]" file="[job_executor.go:142]" Nov 22 18:33:29 callisto forgejo-runner[69307]: time="2025-11-22T18:33:29+01:00" level=debug msg=stopHostEnvironment func="[stopHostEnvironment]" file="[run_context.go:257]" Nov 22 18:33:39 callisto forgejo-runner[69307]: retry = &config.Retry{MaxRetries:0xa, InitialDelay:100000000, MaxDelay:0} Nov 22 18:34:26 callisto forgejo-runner[69307]: time="2025-11-22T18:34:26+01:00" level=warning msg="ReportLog error: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[RunDaemon]" file="[reporter.go:206]" Nov 22 18:35:26 callisto forgejo-runner[69307]: time="2025-11-22T18:35:26+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:36:26 callisto forgejo-runner[69307]: time="2025-11-22T18:36:26+01:00" level=warning msg="ReportState error: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateTask\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[RunDaemon]" file="[reporter.go:210]" Nov 22 18:37:26 callisto forgejo-runner[69307]: time="2025-11-22T18:37:26+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:38:26 callisto forgejo-runner[69307]: time="2025-11-22T18:38:26+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:39:27 callisto forgejo-runner[69307]: time="2025-11-22T18:39:27+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:40:28 callisto forgejo-runner[69307]: time="2025-11-22T18:40:28+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:41:29 callisto forgejo-runner[69307]: time="2025-11-22T18:41:29+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:42:33 callisto forgejo-runner[69307]: time="2025-11-22T18:42:33+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:43:39 callisto forgejo-runner[69307]: time="2025-11-22T18:43:39+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:44:52 callisto forgejo-runner[69307]: time="2025-11-22T18:44:52+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:46:18 callisto forgejo-runner[69307]: time="2025-11-22T18:46:18+01:00" level=warning msg="uploading final logs failed, but will be retried: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func2]" file="[reporter.go:314]" Nov 22 18:46:18 callisto forgejo-runner[69307]: time="2025-11-22T18:46:18+01:00" level=error msg="unable to send final job logs and status: All attempts fail:\n#1: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#2: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#3: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#4: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#5: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#6: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#7: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#8: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#9: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)\n#10: deadline_exceeded: Post \"https://codeberg.org/api/actions/runner.v1.RunnerService/UpdateLog\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" func="[func1]" file="[runner.go:174]" ```
Owner

OK, great information... so the 60s HTTP timeout is active and it's being hit, and the Go process thinks that it's waiting for a server response. The retries are occurring with slightly increasing delays as expected.

My first thought here is that if, at the very end of the CI process, the last step dumps a large volume of log output, that could cause the "uploading final logs" to be a large upload and be more likely to hit a timeout. But when I look at your riscv64-linux-release builds, I don't see any sign of sudden large log dumps at the end of the jobs. And since the log you posted has a "🏁 Job succeeded", I can't imagine there were any large outputs, as I'm looking at other successful jobs that don't have any additional large output. So, this line of thinking doesn't seem helpful.

It's unexpected to me that the process thinks it is waiting for Codeberg to respond -- exceeded while awaiting headers seems to reflect that the Go HTTP client wrote its request to the network buffers and it is waiting on a read from the remote. 🤔 It's possible that the request is actually still outgoing in kernel network buffers or something like that, but a minute for a small post request... feels like we're missing a real root cause here.

I'm thinking two reconfiguration steps that might help:

  • The runner environment variable FORGEJO_CLIENT_TIMEOUT is the timeout being hit here, and it is a default 60 seconds. Increase it... maybe you can find some evidence from successful runs in the logs about how long it could take?
  • The retries might have a chance to be successful if there's something about the system performance that might settle after some idle time -- system cooling, or maybe network congestion. Try bumping runner.report_retry.initial_delay up from 100ms, perhaps to even 1m.
OK, great information... so the 60s HTTP timeout is active and it's being hit, and the Go process thinks that it's waiting for a server response. The retries are occurring with slightly increasing delays as expected. My first thought here is that if, at the very end of the CI process, the last step dumps a large volume of log output, that could cause the "uploading final logs" to be a large upload and be more likely to hit a timeout. But when I look at your riscv64-linux-release builds, I don't see any sign of sudden large log dumps at the end of the jobs. And since the log you posted has a "🏁 Job succeeded", I can't imagine there were any large outputs, as I'm looking at other successful jobs that don't have any additional large output. So, this line of thinking doesn't seem helpful. It's unexpected to me that the process thinks it is waiting for Codeberg to respond -- `exceeded while awaiting headers` seems to reflect that the Go HTTP client wrote its request to the network buffers and it is waiting on a read from the remote. 🤔 It's possible that the request is actually still outgoing in kernel network buffers or something like that, but a minute for a small post request... feels like we're missing a real root cause here. I'm thinking two reconfiguration steps that might help: - The runner environment variable `FORGEJO_CLIENT_TIMEOUT` is the timeout being hit here, and it is a default 60 seconds. Increase it... maybe you can find some evidence from successful runs in the logs about how long it could take? - The retries might have a chance to be successful if there's something about the system performance that might settle after some idle time -- system cooling, or maybe network congestion. Try bumping `runner.report_retry.initial_delay` up from `100ms`, perhaps to even `1m`.
Author
Contributor
  • The retries might have a chance to be successful if there's something about the system performance that might settle after some idle time -- system cooling, or maybe network congestion. Try bumping runner.report_retry.initial_delay up from 100ms, perhaps to even 1m.

I was thinking something similar. I'll try a silly value like 10 minutes for starters; if that works, we're definitely onto something. Failing that, I'll give FORGEJO_CLIENT_TIMEOUT a try.

> * The retries might have a chance to be successful if there's something about the system performance that might settle after some idle time -- system cooling, or maybe network congestion. Try bumping `runner.report_retry.initial_delay` up from `100ms`, perhaps to even `1m`. I was thinking something similar. I'll try a silly value like 10 minutes for starters; if *that* works, we're definitely onto something. Failing that, I'll give `FORGEJO_CLIENT_TIMEOUT` a try.
Owner

Note that retry time will double on each retry, so you might want to set the max retry time to the same value, or drop the number of retry times, or both. 👍

Note that retry time will double on each retry, so you might want to set the max retry time to the same value, or drop the number of retry times, or both. 👍
Author
Contributor

No dice; still just times out. But I notice that when this happens, ssh and scp also become super flaky even though nothing is going on on the machine almost an hour after the job finished - and more interestingly, dmesg says the link went down momentarily a couple of times.

I think this is starting to point towards faulty networking equipment. For now, I've swapped the cable to the second Ethernet port to see if that makes any difference; next step will be replacing the cable. I'll report back on that, though at this point I'm reasonably confident it'll turn out that there's no runner issue here.

No dice; still just times out. But I notice that when this happens, `ssh` and `scp` also become super flaky even though nothing is going on on the machine almost an hour after the job finished - and more interestingly, `dmesg` says the link went down momentarily a couple of times. I think this is starting to point towards faulty networking equipment. For now, I've swapped the cable to the second Ethernet port to see if that makes any difference; next step will be replacing the cable. I'll report back on that, though at this point I'm *reasonably* confident it'll turn out that there's no runner issue here.
Author
Contributor

After changing Ethernet port, the runner went from

image

to

image

with the failure there being a legitimate failure, and no more general network slowness. So I think we have our answer.

After changing Ethernet port, the runner went from ![image](/attachments/2ce7e175-7474-4c9e-9074-a00ce5d2265f) to ![image](/attachments/1d087dd1-5398-4fd3-bf6c-d2e8e0d8b249) with the failure there being a legitimate failure, and no more general network slowness. So I think we have our answer.
484 KiB
233 KiB
Owner

Excellent, good to hear! 👍

Excellent, good to hear! 👍
Sign in to join this conversation.
No milestone
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
forgejo/runner#1166
No description provided.